1. Introduction and Aims

Two datasets were generated using the 10X Genomics Chromium 3’ scRNA-Seq platform:

species experiment_name run_number lane_number sequencer approximate_number_of_cells
pb straight bleed experiment 22252 5 Hiseq 4000 30,000
pb 1:1 mix experiment 24284 1 & 2 Hiseq 2500 5,000

This script details the quality control of the 1:1 mix experiment

This data has been processed using CellRanger into counts tables. This initial analysis gave the following metrics:

Pb 1:1 mix experiment (run #: 24284 lanes 1 and 2 (Hiseq 2500)):

We will load this data in and for each run:

A. Define ‘cells’

B. Filter poor quality cells out

C. Dimensionality Reduction and Clustering

D. Remove Doublets

E. Predict life Cycle Stage (Using Bulk RNA-Seq Correlation)

2. Read in the data

Load the required packages

[1] "Seurat is loaded correctly"
[1] "cowplot is loaded correctly"
[1] "gridExtra is loaded correctly"
[1] "grid is loaded correctly"
[1] "Hmisc is loaded correctly"
[1] "dplyr is loaded correctly"
[1] "scales is loaded correctly"
[1] "ggpubr is loaded correctly"
[1] "patchwork is loaded correctly"

Import GTF file

This will be helpful later on. This contains annotations for each gene:

##Import gtf file:
gtf <- read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/Pberghei.gtf", sep="\t", header = FALSE)
head(gtf)

Read in the Data

## read in 10x output 
tenx5k_raw_data <- Read10X("/Users/Andy/GCSKO/GCSKO_analysis_git/data/10X/tenx_24284")

## Create Seurat object
tenx5k <- CreateSeuratObject(counts = tenx5k_raw_data, min.cells = 0, min.features = 0, project = "GCSKO")
Feature names cannot have underscores ('_'), replacing with dashes ('-')
## add experiment to meta data for merging later
tenx5k@meta.data$experiment <- "tenx5k"

## inspect
tenx5k
An object of class Seurat 
5098 features across 737280 samples within 1 assay 
Active assay: RNA (5098 features, 0 variable features)

3. Defining Cells vs. Background

Plot a knee plot and then use a mixture model to define where the cells vs. background lie

## interesting reference material for this section can be found here: https://hemberg-lab.github.io/scRNA.seq.course/processing-raw-scrna-seq-data.html 

## get the nUMIs
umi_per_barcode <- as.data.frame(tenx5k@meta.data$nCount_RNA)

## remove zeros as these have issues when you log them and make the model later:
umi_per_barcode <- as.data.frame(umi_per_barcode[!(umi_per_barcode$`tenx5k@meta.data$nCount_RNA`==0), ])

## get a rank for each barcode
barcode_rank <- rank(-umi_per_barcode[,1])

## then make into a list
lib_size <- (umi_per_barcode[,1])

## then log this
log_lib_size <- log10(umi_per_barcode[,1])

##plot
#plot(barcode_rank, log_lib_size, xlim=c(1,100000))

## order the barcode ranks
o <- order(barcode_rank)

## reorder the library size, barcode rank by their rank
log_lib_size <- log_lib_size[o]
barcode_rank <- barcode_rank[o]
lib_size <- lib_size[o]

make a mixture model to determine the knee of the plot

## set a seed for the mixture model
set.seed(-92497)

## mixture model calculation 
require("mixtools")
mix <- normalmixEM(log_lib_size)
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
number of iterations= 17 
## plot result
plot(mix, which=2, xlab2="log(mol per cell)")

Find where the distributions intersect (i.e. where cells vs. background is)

## identify where the split between the distributions is
p1 <- dnorm(log_lib_size, mean=mix$mu[1], sd=mix$sigma[1])
p2 <- dnorm(log_lib_size, mean=mix$mu[2], sd=mix$sigma[2])
if (mix$mu[1] < mix$mu[2]) {
    split <- min(log_lib_size[p2 > p1])
} else {
    split <- min(log_lib_size[p1 > p2])
}

## print split
split
[1] 1.724276

View the initial result

## log the barcode rank
log_barcode_rank <- log10(barcode_rank)

## plot
plot(log_barcode_rank, log_lib_size, xlim=c(1,6))
## add the split as a line on the plot
abline(h=split, col="red")

Final Figures:

## make the results of the above functions into a dataframe
df_barcodes <- as.data.frame(cbind(barcode_rank, log_lib_size, lib_size), row.names = NULL)

## add a column for if it is a cell or not
df_barcodes$cell = rownames(df_barcodes) %in% which(df_barcodes$log_lib_size > split)

## change value to a numeric
df_barcodes$cell <- as.numeric(df_barcodes$cell)

## change the 0 to a 2 for ease of handling
df_barcodes$cell[df_barcodes$cell<1] <- 2

## rename the numerics into cells or background
df_barcodes$cell[df_barcodes$cell == 1] <- "Cells"
df_barcodes$cell[df_barcodes$cell == 2] <- "Background"

## extract the cutoff for cells do you can plot the lines
boundary <- as.numeric(sum(df_barcodes$cell == "Cells"))
split <- 10^split

## make the plot
barcode_plot <- ggplot(df_barcodes, aes(x=barcode_rank, y=lib_size, colour = cell, theme_size = 40)) +
  ## make into a dot plot
  geom_point(size = 1, shape = 16) +
  ## make the axis into log and specify breaks
  scale_x_log10(breaks = trans_breaks("log10", function(x) 10^x), labels = trans_format("log10", math_format(10^.x))) +
  ## make the axis into log and specify breaks
  scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x), labels = trans_format("log10", math_format(10^.x))) +
  annotation_logticks() +
  ## change colours of plot
  scale_color_manual(values=c("#bdbdbd", "#5ba43a"), labels = c("Background", "Cells")) +
  ## change aes of legend
  theme(legend.position="bottom", text = element_text(size=25), legend.text=element_text(size=25), axis.text=element_text(size=25)) +
  ## add the lines on the plot
  geom_segment(aes(x = 0, y = split, xend = boundary, yend = split), colour = "black", alpha = 0.01) +
  geom_segment(aes(x = boundary, y = 0, xend = boundary, yend = split), colour = "black", alpha = 0.01) +
  ## change the axis labels
  labs(x = "Barcodes", y = "UMI Counts", colour="Cell Designation") +
  ## change the size of the legend
  guides(colour = guide_legend(override.aes = list(size=10))) +
  ## fix axis
  coord_fixed() +
  ## make it look pretty
  theme_light()

## print the plot
barcode_plot

## save the plot
ggsave("QC_10X_barcode_plot_5k.png", plot = barcode_plot, device = "png", height = 15, width = 15, units = "cm", path = "/Users/Andy/GCSKO/GCSKO_analysis_git/images_to_export/")

so the number of cells that is retained is:


Background      Cells 
    138879       7762 

Filter the object

## extract the nCount and row names from the Seurat object
upb <- data.frame(nCount_RNA = tenx5k@meta.data$nCount_RNA, row.names = rownames(tenx5k@meta.data))

## make blank column for rank
upb$rank <- NA

## order by nCounts
order.scores <- order(upb$nCount_RNA, decreasing = TRUE)

## add a rank to this column based on the ordered nCount
upb$rank[order.scores] <- 1:nrow(upb)

## inspect
#head(upb)

## make a list of cells to retain in the Seurat object
keep_cells <- rownames(upb[which(upb$rank < 7763),])

## subset Seurat object to include cells and discard background
pb_sex <- subset(tenx5k, cells = keep_cells)

4. Filter Out Poor-Quality Cells

Filter Mitochondrial %

Mitochondrial cell counts

## extract mitochondrial genes 
#mito_genes <- gtf[which(gtf$V3 == "rRNA"),]$V9
#mito_genes <- gsub(";.*","", gsub("gene_id ", "", mito_genes))
#paste("These are the mitochondrial genes")
#head(mito_genes)

## extract mito genes
mito_genes <- pb_sex@assays$RNA@counts@Dimnames[[1]][grep("^PBANKA-MIT",pb_sex@assays$RNA@counts@Dimnames[[1]])]

## plot the genes individually
VlnPlot(object = pb_sex, features = mito_genes, pt.size = 0.01)
All cells have the same value of PBANKA-MIT0010.All cells have the same value of PBANKA-MIT0020.All cells have the same value of PBANKA-MIT0030.All cells have the same value of PBANKA-MIT0040.All cells have the same value of PBANKA-MIT0050.All cells have the same value of PBANKA-MIT0090.All cells have the same value of PBANKA-MIT0130.All cells have the same value of PBANKA-MIT0140.All cells have the same value of PBANKA-MIT0150.All cells have the same value of PBANKA-MIT0160.All cells have the same value of PBANKA-MIT0200.All cells have the same value of PBANKA-MIT0210.All cells have the same value of PBANKA-MIT0230.All cells have the same value of PBANKA-MIT0240.All cells have the same value of PBANKA-MIT0250.All cells have the same value of PBANKA-MIT0260.All cells have the same value of PBANKA-MIT0290.All cells have the same value of PBANKA-MIT0300.All cells have the same value of PBANKA-MIT0320.All cells have the same value of PBANKA-MIT0330.All cells have the same value of PBANKA-MIT0340.All cells have the same value of PBANKA-MIT0370.

## make a percentage mitocondrial for each cell (this will work as long as you filter cells out with zero counts)
pb_sex <- PercentageFeatureSet(pb_sex, pattern = "^PBANKA-MIT", col.name = "percent.mt")

plot percentage mitochondrial

## plot for percentage of mitochondrial reads
v1 <- VlnPlot(object = pb_sex, features = "percent.mt", pt.size = 0.01) +
  ## add a line where we will filter
  geom_abline(intercept = 20, col="blue") +
  ## change labels
  labs(x = "",y = "% Mitochondrial Reads", title = "Mitochondrial per cell") +
  ## remove legend
  theme(legend.position = "none") +
  ## change appearance
  theme_classic() +
  scale_fill_manual(values="grey") +
  #scale_y_continuous(limits = c(0, 100)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))

## print
v1


## save
#ggsave("~/images_to_export/QC_10X_mito_violin.png", plot = QC_mito_violin, device = "png", path = NULL, scale = 1, width = 15, height = 10, units = "cm", dpi = 300, limitsize = TRUE)

In the Smart-seq2 data, we use a threshold of 20%. No cell in our 10X data is higher than 13% and only

[1] 10

cells have a % mitochondrial reads above 5%.

nGenes filter

plot individual violin plots

## nGenes plot
gene_plot_5k <- VlnPlot(object = pb_sex, features = "nFeature_RNA", pt.size = 0.01)

## improve the aesthetics
gene_plot_5k <- gene_plot_5k + 
  geom_abline(intercept = 200, col="blue") +
  labs(x = "",y = "nGene", title = "Genes per cell") +
  theme_classic() +
  scale_fill_manual(values="grey") +
  scale_y_continuous(limits = c(0, 3000)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))
Scale for 'y' is already present. Adding another scale for 'y', which will
replace the existing scale.
## nUMI plot
numi_plot_5k <- VlnPlot(object = pb_sex, features = "nCount_RNA", pt.size = 0.01)

## improve aesthetics
numi_plot_5k <- numi_plot_5k +
  labs(x = "",y = "nUMI", title = "UMIs per cell") +
  theme_classic() +
  scale_fill_manual(values="grey") +
  scale_y_continuous(limits = c(0, 3000)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))
Scale for 'y' is already present. Adding another scale for 'y', which will
replace the existing scale.
## plot together
grid.arrange(gene_plot_5k, numi_plot_5k, ncol = 2, top=textGrob("5K cells 10X", gp=gpar(fontsize=15,font=8)))


## save nGene plot on its own
#ggsave("QC_ngene_plot.pdf", plot = gene1, device = "pdf", height = 5, width = 5, units = "in", path = "/Users/ar19/Desktop/PhD/GCSKO_Analysis")

plot two metrics together

## make a dataframe for important filtering metrics
df <- data.frame(nCount = log10(pb_sex@meta.data$nCount_RNA), nGenes = pb_sex@meta.data$nFeature_RNA, percent_mt = pb_sex@meta.data$percent.mt, experiment = pb_sex@meta.data$experiment)

## plot main dotplot
plot1 <- ggplot(df, aes(x = nCount, y = nGenes)) +
  #geom_point(aes(), size = 0.1) +
  geom_hex(bins = 200) +
  #geom_rug() + 
  scale_y_continuous(name = "Number of Detected Genes") + 
  scale_x_continuous(name = "log10(Number of Total UMI)") + 
  theme_pubr() +
  theme(legend.position = "bottom") +
  geom_hline(yintercept=200)

## plot density plot x
dens <- density(df$nCount)
dt <- data.frame(x=dens$x, y=dens$y)
probs <- c(0, 0.25, 0.5, 0.75, 1)
quantiles <- quantile(df$nCount, prob=probs)
dt$quant <- factor(findInterval(dt$x,quantiles))
dens1 <- ggplot(dt, aes(x,y)) + geom_line() + geom_ribbon(aes(ymin=0, ymax=y, fill=quant)) + scale_x_continuous(breaks=quantiles) + scale_fill_brewer(guide="none") + theme_void() + theme(legend.position = "none")
## plot density plot y
## old method
# dens2 <- ggplot(df, aes(x = nGenes, y = experiment)) +
#   geom_density(alpha = 0.2) +
#   theme_void() +
#   theme(legend.position = "none") +
#   coord_flip()
## new method
dens <- density(df$nGenes)
dt <- data.frame(x=dens$x, y=dens$y)
probs <- c(0, 0.25, 0.5, 0.75, 1)
quantiles <- quantile(df$nGenes, prob=probs)
dt$quant <- factor(findInterval(dt$x,quantiles))
dens2 <- ggplot(dt, aes(x,y)) + geom_line() + geom_ribbon(aes(ymin=0, ymax=y, fill=quant)) + scale_x_continuous(breaks=quantiles) + scale_fill_brewer(guide="none") + theme_void() + theme(legend.position = "none") +
  coord_flip()

## plot together
QC_composite_plot <- dens1 + plot_spacer() + plot1 + dens2 + plot_layout(ncol = 2, nrow = 2, widths = c(4, 1), heights = c(1, 4))

## print
QC_composite_plot


## save
ggsave("/Users/Andy/GCSKO/GCSKO_analysis_git/images_to_export/QC_10X_composite_plot.png", plot = QC_composite_plot, device = "png", path = NULL, scale = 1, width = 15, height = 15, units = "cm", dpi = 300, limitsize = TRUE)

Filtering

The threshold used in the malaria cell atlas was 230 for Pb but this is dependent on sequencing depth etc. We can plot the number of cells recovered for a range of thresholds:

paste("original number of cells =", nrow(pb_sex@meta.data))
[1] "original number of cells = 7762"
paste("with >150 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 150, ]))
[1] "with >150 filter = 7188"
paste("with >200 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 200, ]))
[1] "with >200 filter = 6631"
paste("with >230 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 230, ]))
[1] "with >230 filter = 6239"

Since we have already filtered on nUMI, we will filter with 200.

## number of cells before filtering
pb_sex_pre_filter_nCells <- nrow(pb_sex@meta.data)
## filter object
pb_sex <- subset(pb_sex, subset = nFeature_RNA > 200)
## number of cells after filtering
pb_sex_post_filter_nCells <- nrow(pb_sex@meta.data)
## print results of filtering
paste("number of cells pre-filter", pb_sex_pre_filter_nCells)
[1] "number of cells pre-filter 7762"
paste("number of cells post-filter", pb_sex_post_filter_nCells)
[1] "number of cells post-filter 6631"
paste((pb_sex_pre_filter_nCells - pb_sex_post_filter_nCells), "cells were removed by filtering on number of genes.")
[1] "1131 cells were removed by filtering on number of genes."

5. Dimensionality Reduction and Clustering

Prepare data

## normalise object
pb_sex <- NormalizeData(pb_sex, normalization.method = "LogNormalize", scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
## find variable genes
pb_sex <- FindVariableFeatures(pb_sex, selection.method = "vst", nfeatures = 2000)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
## scale the data
all.genes <- rownames(pb_sex)
pb_sex <- ScaleData(pb_sex, features = all.genes)
Centering and scaling data matrix

  |                                                                              
  |                                                                        |   0%
  |                                                                              
  |============                                                            |  17%
  |                                                                              
  |========================                                                |  33%
  |                                                                              
  |====================================                                    |  50%
  |                                                                              
  |================================================                        |  67%
  |                                                                              
  |============================================================            |  83%
  |                                                                              
  |========================================================================| 100%

PCA

## run PCA
pb_sex <- RunPCA(pb_sex, features = VariableFeatures(object = pb_sex))
PC_ 1 
Positive:  PBANKA-1235600, PBANKA-0931300, PBANKA-1214300, PBANKA-1400400, PBANKA-1101100, PBANKA-0205000, PBANKA-0311800, PBANKA-0823400, PBANKA-1242300, PBANKA-1246600 
       PBANKA-0722600, PBANKA-1008500, PBANKA-1308600, PBANKA-0932400, PBANKA-1314800, PBANKA-1300096, PBANKA-1309900, PBANKA-1200096, PBANKA-1145400, PBANKA-0722801 
       PBANKA-1100441, PBANKA-1040521, PBANKA-1326400, PBANKA-1101300, PBANKA-0941300, PBANKA-1210200, PBANKA-1303800, PBANKA-0216061, PBANKA-0406500, PBANKA-1127000 
Negative:  PBANKA-1312700, PBANKA-1224900, PBANKA-0824400, PBANKA-1453000, PBANKA-1115200, PBANKA-1432200, PBANKA-1204200, PBANKA-0703500, PBANKA-0702000, PBANKA-0812600 
       PBANKA-0312700, PBANKA-1128800, PBANKA-1218300, PBANKA-1106500, PBANKA-1430600, PBANKA-1449300, PBANKA-0209300, PBANKA-1232600, PBANKA-1432400, PBANKA-1421500 
       PBANKA-1320800, PBANKA-0620400, PBANKA-1038800, PBANKA-1110700, PBANKA-1336200, PBANKA-0417600, PBANKA-1451100, PBANKA-1143800, PBANKA-1414500, PBANKA-0402700 
PC_ 2 
Positive:  PBANKA-1431500, PBANKA-1431400, PBANKA-0942400, PBANKA-1340400, PBANKA-0934700, PBANKA-1038700, PBANKA-0942300, PBANKA-1449000, PBANKA-1208800, PBANKA-0828900 
       PBANKA-0911000, PBANKA-0306100, PBANKA-0927700, PBANKA-0806800, PBANKA-0416100, PBANKA-0507800, PBANKA-1409600, PBANKA-0411300, PBANKA-0406200, PBANKA-0415800 
       PBANKA-1033700, PBANKA-1361500, PBANKA-0925400, PBANKA-0615200, PBANKA-1108800, PBANKA-0507300, PBANKA-0720100, PBANKA-0920700, PBANKA-1030400, PBANKA-0510600 
Negative:  PBANKA-1317200, PBANKA-1352500, PBANKA-0810700, PBANKA-1225500, PBANKA-1319500, PBANKA-0402500, PBANKA-0827400, PBANKA-1419300, PBANKA-0714000, PBANKA-1315300 
       PBANKA-0105100, PBANKA-1436300, PBANKA-1311400, PBANKA-0417600, PBANKA-0821400, PBANKA-0417200, PBANKA-0204500, PBANKA-1342300, PBANKA-0704900, PBANKA-1415200 
       PBANKA-0605800, PBANKA-1115000, PBANKA-0517600, PBANKA-1329900, PBANKA-1134900, PBANKA-1231300, PBANKA-1233600, PBANKA-1035200, PBANKA-0912500, PBANKA-1363600 
PC_ 3 
Positive:  PBANKA-1400600, PBANKA-1459300, PBANKA-0416000, PBANKA-0304800, PBANKA-0305100, PBANKA-1365200, PBANKA-1443300, PBANKA-0830200, PBANKA-0938400, PBANKA-0827200 
       PBANKA-1137800, PBANKA-1437300, PBANKA-0932200, PBANKA-0506100, PBANKA-1349000, PBANKA-0313800, PBANKA-1113300, PBANKA-0409800, PBANKA-1017100, PBANKA-1117000 
       PBANKA-0408500, PBANKA-1450300, PBANKA-0922500, PBANKA-0934300, PBANKA-0505200, PBANKA-0941800, PBANKA-0915200, PBANKA-0722921, PBANKA-0316600, PBANKA-0519400 
Negative:  PBANKA-1431500, PBANKA-1431400, PBANKA-1449000, PBANKA-0806800, PBANKA-0927700, PBANKA-0911000, PBANKA-1108000, PBANKA-0828900, PBANKA-0601200, PBANKA-1208800 
       PBANKA-1409600, PBANKA-0830900, PBANKA-0942400, PBANKA-1029400, PBANKA-1038700, PBANKA-0102700, PBANKA-1316500, PBANKA-1304500, PBANKA-0521200, PBANKA-1333100 
       PBANKA-0902400, PBANKA-1129600, PBANKA-1119800, PBANKA-0822900, PBANKA-1109600, PBANKA-0301800, PBANKA-1429100, PBANKA-0622000, PBANKA-1038200, PBANKA-0942300 
PC_ 4 
Positive:  PBANKA-0107300, PBANKA-1214300, PBANKA-0814200, PBANKA-0713300, PBANKA-0604300, PBANKA-0405200, PBANKA-0109100, PBANKA-0932200, PBANKA-0823400, PBANKA-1460400 
       PBANKA-1448000, PBANKA-0416500, PBANKA-0938400, PBANKA-0406500, PBANKA-1302800, PBANKA-1130500, PBANKA-1437300, PBANKA-1444100, PBANKA-0211500, PBANKA-1419100 
       PBANKA-0710100, PBANKA-1223100, PBANKA-1235600, PBANKA-1450300, PBANKA-0919600, PBANKA-0916200, PBANKA-1242300, PBANKA-1326400, PBANKA-0313800, PBANKA-0818900 
Negative:  PBANKA-0519000, PBANKA-0519100, PBANKA-0519300, PBANKA-0831000, PBANKA-1349100, PBANKA-1344400, PBANKA-1002400, PBANKA-0713100, PBANKA-0523700, PBANKA-0111000 
       PBANKA-0519400, PBANKA-1349000, PBANKA-1032100, PBANKA-1014500, PBANKA-0932000, PBANKA-0804500, PBANKA-1315700, PBANKA-0501400, PBANKA-1425900, PBANKA-0519200 
       PBANKA-1101400, PBANKA-0509600, PBANKA-0619700, PBANKA-1035400, PBANKA-0417800, PBANKA-1210600, PBANKA-1327100, PBANKA-0523800, PBANKA-0307500, PBANKA-1117200 
PC_ 5 
Positive:  PBANKA-0501600, PBANKA-1240600, PBANKA-1002600, PBANKA-0707700, PBANKA-0112200, PBANKA-1002400, PBANKA-0208900, PBANKA-0307500, PBANKA-0812200, PBANKA-1423000 
       PBANKA-0832800, PBANKA-1119600, PBANKA-1409200, PBANKA-0716900, PBANKA-0100400, PBANKA-1364400, PBANKA-1347000, PBANKA-0915000, PBANKA-0819600, PBANKA-1319000 
       PBANKA-1212500, PBANKA-1400091, PBANKA-0900900, PBANKA-1202000, PBANKA-1345900, PBANKA-0311500, PBANKA-1008600, PBANKA-0911700, PBANKA-1349100, PBANKA-0519200 
Negative:  PBANKA-1443300, PBANKA-0925600, PBANKA-0306700, PBANKA-1106500, PBANKA-0907200, PBANKA-0304800, PBANKA-1217400, PBANKA-1107600, PBANKA-1349000, PBANKA-1319300 
       PBANKA-1313100, PBANKA-0312500, PBANKA-0611700, PBANKA-1113300, PBANKA-0208000, PBANKA-1241800, PBANKA-0305100, PBANKA-1438000, PBANKA-1339300, PBANKA-0304400 
       PBANKA-0833300, PBANKA-0206300, PBANKA-0207500, PBANKA-0519400, PBANKA-0715300, PBANKA-1360000, PBANKA-0112100, PBANKA-1340500, PBANKA-0915200, PBANKA-1416800 
## plot 
DimPlot(pb_sex, reduction = "pca")


## elbow plot
ElbowPlot(pb_sex, ndims = 30, reduction = "pca")

UMAP

## run UMAP
pb_sex <- RunUMAP(pb_sex, dims = 1:10, seed.use = 1234, n.neighbors = 150)
20:34:58 UMAP embedding parameters a = 0.9922 b = 1.112
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:34:58 Read 6631 rows and found 10 numeric columns
20:34:58 Using Annoy for neighbor search, n_neighbors = 150
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:34:58 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:35:00 Writing NN index file to temp file /var/folders/wj/rztzclxn1t10cl2sk0plbf3r0000gn/T//RtmpXGhNV1/file433740c39e54
20:35:00 Searching Annoy index using 1 thread, search_k = 15000
20:35:10 Annoy recall = 100%
20:35:11 Commencing smooth kNN distance calibration using 1 thread
20:35:15 Initializing from normalized Laplacian + noise
20:35:18 Commencing optimization for 500 epochs, with 1163176 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:35:34 Optimization finished
## plot
DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)


## These are the parameters used in the merge UMAP
#pb_sex <- RunUMAP(pb_sex, reduction = "pca", dims = 1:10, n.neighbors = 150, seed.use = 1234, min.dist = 0.4, repulsion.strength = 0.03, local.connectivity = 150)
#DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)

colour with a few marker genes:

# PBANKA-0515000 - p25 - female
# PBANKA-1212600 - HAP2 - male
# PBANKA-0600600 - NEK3 - male
# PBANKA-0831000 - MSP1 - late asexual
# PBANKA-1315700 - RON2 - (asexuals and some male?)
# PBANKA-0416100 - dynenin heavy chain - male - used in 820 line
# PBANKA-1319500 - CCP2 - female - used in 820 line 
# PBANKA-1437500 - AP2-G - seuxal commitment gene
# PBANKA-1102200 - MSP8 - early asexual (from Bozdech paper)

FeaturePlot(pb_sex, features = c("PBANKA-0515000", "PBANKA-1212600","PBANKA-0600600", "PBANKA-0831000", "PBANKA-1315700", "PBANKA-0416100", "PBANKA-1319500", "PBANKA-1437500", "PBANKA-1102200"))

Clustering

pb_sex <- FindNeighbors(pb_sex, dims = 1:21)
Computing nearest neighbor graph
Computing SNN
pb_sex <- FindClusters(pb_sex, resolution = 1)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 6631
Number of edges: 277150

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8104
Number of communities: 14
Elapsed time: 1 seconds

6. Remove Doublets

DoubletFinder

DoubletFinder function

## DoubletFinder should be able to be installed and run as so:
#devtools::install_github('chris-mcginnis-ucsf/DoubletFinder')
#library(doubletFinder) #allows removal of doublets
## but there seems to be some problems with this so we will run it from the github code

## the doublet finder function
doubletFinder_v3 <- function(seu, PCs, pN = 0.25, pK, nExp, reuse.pANN = FALSE, sct = FALSE) {
  require(Seurat); require(fields); require(KernSmooth)

  ## Generate new list of doublet classificatons from existing pANN vector to save time
  if (reuse.pANN != FALSE ) {
    pANN.old <- seu@meta.data[ , reuse.pANN]
    classifications <- rep("Singlet", length(pANN.old))
    classifications[order(pANN.old, decreasing=TRUE)[1:nExp]] <- "Doublet"
    seu@meta.data[, paste("DF.classifications",pN,pK,nExp,sep="_")] <- classifications
    return(seu)
  }

  if (reuse.pANN == FALSE) {
    ## Make merged real-artifical data
    real.cells <- rownames(seu@meta.data)
    data <- seu@assays$RNA@counts[, real.cells]
    n_real.cells <- length(real.cells)
    n_doublets <- round(n_real.cells/(1 - pN) - n_real.cells)
    print(paste("Creating",n_doublets,"artificial doublets...",sep=" "))
    real.cells1 <- sample(real.cells, n_doublets, replace = TRUE)
    real.cells2 <- sample(real.cells, n_doublets, replace = TRUE)
    doublets <- (data[, real.cells1] + data[, real.cells2])/2
    colnames(doublets) <- paste("X", 1:n_doublets, sep = "")
    data_wdoublets <- cbind(data, doublets)

    ## Store important pre-processing information
    orig.commands <- seu@commands

    ## Pre-process Seurat object
    if (sct == FALSE) {
      print("Creating Seurat object...")
      seu_wdoublets <- CreateSeuratObject(counts = data_wdoublets)

      print("Normalizing Seurat object...")
      seu_wdoublets <- NormalizeData(seu_wdoublets,
                                     normalization.method = orig.commands$NormalizeData.RNA@params$normalization.method,
                                     scale.factor = orig.commands$NormalizeData.RNA@params$scale.factor,
                                     margin = orig.commands$NormalizeData.RNA@params$margin)

      print("Finding variable genes...")
      seu_wdoublets <- FindVariableFeatures(seu_wdoublets,
                                            selection.method = orig.commands$FindVariableFeatures.RNA$selection.method,
                                            loess.span = orig.commands$FindVariableFeatures.RNA$loess.span,
                                            clip.max = orig.commands$FindVariableFeatures.RNA$clip.max,
                                            mean.function = orig.commands$FindVariableFeatures.RNA$mean.function,
                                            dispersion.function = orig.commands$FindVariableFeatures.RNA$dispersion.function,
                                            num.bin = orig.commands$FindVariableFeatures.RNA$num.bin,
                                            binning.method = orig.commands$FindVariableFeatures.RNA$binning.method,
                                            nfeatures = orig.commands$FindVariableFeatures.RNA$nfeatures,
                                            mean.cutoff = orig.commands$FindVariableFeatures.RNA$mean.cutoff,
                                            dispersion.cutoff = orig.commands$FindVariableFeatures.RNA$dispersion.cutoff)

      print("Scaling data...")
      seu_wdoublets <- ScaleData(seu_wdoublets,
                                 features = orig.commands$ScaleData.RNA$features,
                                 model.use = orig.commands$ScaleData.RNA$model.use,
                                 do.scale = orig.commands$ScaleData.RNA$do.scale,
                                 do.center = orig.commands$ScaleData.RNA$do.center,
                                 scale.max = orig.commands$ScaleData.RNA$scale.max,
                                 block.size = orig.commands$ScaleData.RNA$block.size,
                                 min.cells.to.block = orig.commands$ScaleData.RNA$min.cells.to.block)

      print("Running PCA...")
      seu_wdoublets <- RunPCA(seu_wdoublets,
                              features = orig.commands$ScaleData.RNA$features,
                              npcs = length(PCs),
                              rev.pca =  orig.commands$RunPCA.RNA$rev.pca,
                              weight.by.var = orig.commands$RunPCA.RNA$weight.by.var,
                              verbose=FALSE)
      pca.coord <- seu_wdoublets@reductions$pca@cell.embeddings[ , PCs]
      cell.names <- rownames(seu_wdoublets@meta.data)
      nCells <- length(cell.names)
      rm(seu_wdoublets); gc() # Free up memory
    }

    if (sct == TRUE) {
      require(sctransform)
      print("Creating Seurat object...")
      seu_wdoublets <- CreateSeuratObject(counts = data_wdoublets)

      print("Running SCTransform...")
      seu_wdoublets <- SCTransform(seu_wdoublets)

      print("Running PCA...")
      seu_wdoublets <- RunPCA(seu_wdoublets, npcs = length(PCs))
      pca.coord <- seu_wdoublets@reductions$pca@cell.embeddings[ , PCs]
      cell.names <- rownames(seu_wdoublets@meta.data)
      nCells <- length(cell.names)
      rm(seu_wdoublets); gc()
    }

    ## Compute PC distance matrix
    print("Calculating PC distance matrix...")
    dist.mat <- fields::rdist(pca.coord)

    ## Compute pANN
    print("Computing pANN...")
    pANN <- as.data.frame(matrix(0L, nrow = n_real.cells, ncol = 1))
    rownames(pANN) <- real.cells
    colnames(pANN) <- "pANN"
    k <- round(nCells * pK)
    for (i in 1:n_real.cells) {
      neighbors <- order(dist.mat[, i])
      neighbors <- neighbors[2:(k + 1)]
      neighbor.names <- rownames(dist.mat)[neighbors]
      pANN$pANN[i] <- length(which(neighbors > n_real.cells))/k
    }

    print("Classifying doublets..")
    classifications <- rep("Singlet",n_real.cells)
    classifications[order(pANN$pANN[1:n_real.cells], decreasing=TRUE)[1:nExp]] <- "Doublet"
    seu@meta.data[, paste("pANN",pN,pK,nExp,sep="_")] <- pANN[rownames(seu@meta.data), 1]
    seu@meta.data[, paste("DF.classifications",pN,pK,nExp,sep="_")] <- classifications
    return(seu)
  }
}
## usage: https://rdrr.io/github/chris-mcginnis-ucsf/DoubletFinder/man/doubletFinder_v3.html
## source: https://github.com/chris-mcginnis-ucsf/DoubletFinder/blob/master/R/doubletFinder_v3.R

Run DoubletFinder

# the tutorial recommends using this as an approximation:
#nExp_poi <- round(0.15*nrow(pb_sex@meta.data))
#but a more appropriate approximation is that the expected number of doublets is ~1% per 1000 cells so:
nExp_poi <- round((0.01*(nrow(pb_sex@meta.data)/1000))*nrow(pb_sex@meta.data))
#run doublet finder:
pb_sex <- doubletFinder_v3(pb_sex, PCs = 1:21, pN = 0.25, pK = 0.01, nExp = nExp_poi, reuse.pANN = FALSE, sct = FALSE)
[1] "Creating 2210 artificial doublets..."
[1] "Creating Seurat object..."
[1] "Normalizing Seurat object..."
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
[1] "Finding variable genes..."
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
[1] "Scaling data..."
Centering and scaling data matrix

  |                                                                              
  |                                                                        |   0%
  |                                                                              
  |============                                                            |  17%
  |                                                                              
  |========================                                                |  33%
  |                                                                              
  |====================================                                    |  50%
  |                                                                              
  |================================================                        |  67%
  |                                                                              
  |============================================================            |  83%
  |                                                                              
  |========================================================================| 100%
[1] "Running PCA..."
[1] "Calculating PC distance matrix..."
[1] "Computing pANN..."
[1] "Classifying doublets.."

results in:

table(pb_sex@meta.data$DF.classifications_0.25_0.01_440)

Doublet Singlet 
    440    6191 

Validation and visualisation of doublets

visualise where doublets are:

doublet.cells <- c(rownames(pb_sex@meta.data[pb_sex@meta.data$DF.classifications_0.25_0.01_440 == "Doublet",]))
d1 <- DimPlot(pb_sex, reduction = "umap", cells.highlight = doublet.cells, sizes.highlight = 2)
#TSNEPlot(object = pb_sex, cells.highlight = doublet.cells, do.return = TRUE, )
doublet1 <- d1 + coord_fixed() + theme(axis.text.x=element_blank())

## plot clusters
cluster_plot <- DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)

doublet1 + cluster_plot

VlnPlot(object = pb_sex, features = "pANN_0.25_0.01_440", pt.size = 0.1)

## extract meta data cols of interest
df <- pb_sex@meta.data[,c("RNA_snn_res.1","DF.classifications_0.25_0.01_440")]

## make a table from doublets
df <- data.frame(rbind(table(df)))

## ammend rownames
df$cluster <- rownames(df)

## calculate percentages of cells that are doublets
df$pc_doublet <- ((df[,1])/((df[,1]) + df[,2]))*100

## inspect
#kable(df[order(df$pc_doublet),])

## plot 
ggplot(data=df, aes(x=cluster, y=pc_doublet)) +
  geom_col(fill="steelblue") +
  theme_minimal()

Filter doublets

It definitely seems like there are some biases in doublet detection. Fewer doublets in very early rings and in mature sexes may be due to a smaller number of the population being these cells.

It may also be biological, that these cells are less likely to associate to one another (although less likely, as doublets are a result of the probability of two cells being captured inside the same droplet together at a certain loading concentration, rather than two cells already being together upon droplet capture).

remove doublets:

## make a list of singlet cells
keep_singlets <- rownames(pb_sex@meta.data[pb_sex@meta.data$DF.classifications_0.25_0.01_440 == "Singlet",])

## subset into new seurat object
pb_sex_filtered <- subset(pb_sex, cells = keep_singlets, subset.raw = TRUE)
The following arguments are not used: subset.raw
## compare old and new objects
pb_sex
An object of class Seurat 
5098 features across 6631 samples within 1 assay 
Active assay: RNA (5098 features, 2000 variable features)
 2 dimensional reductions calculated: pca, umap
pb_sex_filtered
An object of class Seurat 
5098 features across 6191 samples within 1 assay 
Active assay: RNA (5098 features, 2000 variable features)
 2 dimensional reductions calculated: pca, umap

7. Life Cycle Stage (Using Bulk RNA-Seq Correlation)

Add in bulk data predictions

hoo et al.

#Pb Prediction correlations with bulk data (asexual hoo): 

#Load in required package:
library(Hmisc)
#Cooerce expression data into a matrix and load in the reference timecourse data:
x10 <- as.matrix(pb_sex_filtered@assays$RNA@data)
rownames(x10) <- gsub("-", "_", rownames(x10))
#read in bulk data:
hoo<-as.matrix(read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/hoo_berg2.txt",header=T, row.names=1))
#Make a blank dataframe in which to add prediction:
df <- data.frame(matrix(ncol = 4, nrow = 0))
colnames(df) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
#Do correlations with bulk data using both Spearman and Pearson (and the top 1000 genes):
for (i in 1:ncol(x10))
{
  shared<-intersect(row.names(as.matrix(head(sort(x10[,i], decreasing=TRUE),1000))),row.names(hoo))
  step0<-rcorr(x10[shared,i],hoo[shared,1:12],type = "spearman")
  step1<-as.matrix(t(step0$r[2:13,1]))
  step2<-rcorr(x10[shared,i],hoo[shared,1:12],type = "pearson")
  step3<-as.matrix(t(step2$r[2:13,1]))
  step4<-cbind(colnames(step1)[which.max(step1)],step1[which.max(step1)],colnames(step3)[which.max(step3)],step3[which.max(step3)])
  colnames(step4) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
  rownames(step4)<-colnames(x10)[i]
  df<-rbind(df,step4)
}
#Write out data into a csv file:
#write.csv(dfringr,file="/Users/ar19/Desktop/PhD/AR04_GCSKO_project/All_mutants_Feb_2018/predictionpbcombined.csv")
#Change the format of the output to make it more readable:
#gsub("Pb_","", dfringr[,1]) - Make predictions into 18hr.dat format:

#spearman:
df[,1] <- gsub("Pb_","", df[,1])
#Remove hr.dat from list:
df[,1] <- gsub("hr.dat","", df[,1])
#Check - dfringr[,1]
#Make into a number:
df[,1] <- as.numeric(df[,1])
df[,2] <- as.numeric(as.character(df[,2]))

#pearson:
df[,3] <- gsub("Pb_","", df[,3])
#Remove hr.dat from list:
df[,3] <- gsub("hr.dat","", df[,3])
#Check - dfringr[,1]
#Make into a number:
df[,3] <- as.numeric(df[,3])
df[,4] <- as.numeric(as.character(df[,4]))
#add to 10X object:
pb_sex_filtered <- AddMetaData(pb_sex_filtered, metadata = df)
Invalid name supplied, making object name syntactically valid. New object name is Prediction.Spearman.r.Spearman.Prediction.Pearsons.r.Pearsons.; see ?make.names for more details on syntax validity

Kasia’s data

Can also do with Kasia’s timecourse data:

kas<-as.matrix(read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/AP2OETC.txt",header=T, row.names=1))
#Make a blank dataframe in which to add prediction:
dfs <- data.frame(matrix(ncol = 4, nrow = 0))
colnames(dfs) <- c("ID","Prediction","r (Pearson)")
#Do correlations with bulk data using both Spearman and Pearson (and the top 1000 genes):
for (i in 1:ncol(x10))
{
  shared<-intersect(row.names(as.matrix(head(sort(x10[,i], decreasing=TRUE),1000))),rownames(kas))
  step0<-rcorr(x10[shared,i],kas[shared,1:10],type = "spearman")
  step1<-as.matrix(t(step0$r[2:11,1]))
  step2<-rcorr(x10[shared,i],kas[shared,1:10],type = "pearson")
  step3<-as.matrix(t(step2$r[2:11,1]))
  step4<-cbind(colnames(step1)[which.max(step1)],step1[which.max(step1)],colnames(step3)[which.max(step3)],step3[which.max(step3)])
  colnames(step4) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
  rownames(step4)<-colnames(x10)[i]
  dfs<-rbind(dfs,step4)
}
#Write out data into a csv file:
#write.csv(df,file="/Users/ar19/Desktop/PhD/AR04_GCSKO_project/All_mutants_Feb_2018/predictionkasiacombined.csv")

#Change the format of the output to make it more readable:
#gsub("Pb_","", dfs[,1]) - Make predictions into 18hr.dat format:
dfs[,1] <- gsub("X","", dfs[,1])
#Make into a number:
dfs[,1] <- as.numeric(dfs[,1])
#Make into a number:
dfs[,2] <- as.numeric(as.character(dfs[,2]))

#gsub("Pb_","", dfs[,1]) - Make predictions into 18hr.dat format:
dfs[,3] <- gsub("X","", dfs[,3])
#Make into a number:
dfs[,3] <- as.numeric(dfs[,3])
#dfs[,1]
#Make into a number:
dfs[,4] <- as.numeric(as.character(dfs[,4]))

colnames(dfs) <- c('Prediction(Spearman)_Kasia', 'r(Spearman)_Kasia', 'Prediction(Pearson)_Kasia', 'r(Pearson)_Kasia')
#add to Seurat:
#add to 10X object:
pb_sex_filtered <- AddMetaData(pb_sex_filtered, dfs)
Invalid name supplied, making object name syntactically valid. New object name is Prediction.Spearman._Kasiar.Spearman._KasiaPrediction.Pearson._Kasiar.Pearson._Kasia; see ?make.names for more details on syntax validity

Visualise

Confirm life cycle designations:

## plot
FeaturePlot(pb_sex_filtered, features = c("Prediction.Spearman._Kasia", "Prediction.Spearman."))

optimse UMAP

## PCA calc
pb_sex_filtered <- RunPCA(pb_sex_filtered, features = VariableFeatures(object = pb_sex_filtered))
The following 5 features requested have zero variance (running reduction without them): PBANKA-0612861, PBANKA-0836921, PBANKA-1000041, PBANKA-API0290, PBANKA-API0031PC_ 1 
Positive:  PBANKA-1235600, PBANKA-0931300, PBANKA-1214300, PBANKA-1400400, PBANKA-1101100, PBANKA-0205000, PBANKA-0311800, PBANKA-0823400, PBANKA-1242300, PBANKA-1246600 
       PBANKA-0722600, PBANKA-1008500, PBANKA-0932400, PBANKA-1308600, PBANKA-1314800, PBANKA-1309900, PBANKA-1300096, PBANKA-1145400, PBANKA-1200096, PBANKA-1100441 
       PBANKA-0722801, PBANKA-1101300, PBANKA-1326400, PBANKA-1040521, PBANKA-0941300, PBANKA-1127000, PBANKA-1303800, PBANKA-0406500, PBANKA-1210200, PBANKA-0814200 
Negative:  PBANKA-1312700, PBANKA-1224900, PBANKA-0824400, PBANKA-1115200, PBANKA-1453000, PBANKA-1432200, PBANKA-1204200, PBANKA-0703500, PBANKA-0702000, PBANKA-0812600 
       PBANKA-0312700, PBANKA-1128800, PBANKA-1218300, PBANKA-1232600, PBANKA-1432400, PBANKA-1430600, PBANKA-1449300, PBANKA-1106500, PBANKA-0209300, PBANKA-0620400 
       PBANKA-1421500, PBANKA-1336200, PBANKA-1320800, PBANKA-0417600, PBANKA-1038800, PBANKA-1129800, PBANKA-1414500, PBANKA-1451100, PBANKA-1143800, PBANKA-0402700 
PC_ 2 
Positive:  PBANKA-1431500, PBANKA-1431400, PBANKA-0942400, PBANKA-1340400, PBANKA-1449000, PBANKA-1038700, PBANKA-0942300, PBANKA-0934700, PBANKA-1208800, PBANKA-0828900 
       PBANKA-0911000, PBANKA-0306100, PBANKA-0927700, PBANKA-0806800, PBANKA-1409600, PBANKA-0406200, PBANKA-0411300, PBANKA-0415800, PBANKA-0507800, PBANKA-0416100 
       PBANKA-1033700, PBANKA-1361500, PBANKA-1108800, PBANKA-0920700, PBANKA-0510600, PBANKA-0925400, PBANKA-0720100, PBANKA-0507300, PBANKA-1119800, PBANKA-1030400 
Negative:  PBANKA-1317200, PBANKA-1352500, PBANKA-0827400, PBANKA-0810700, PBANKA-1225500, PBANKA-1319500, PBANKA-0402500, PBANKA-1419300, PBANKA-0714000, PBANKA-1315300 
       PBANKA-1436300, PBANKA-0105100, PBANKA-1311400, PBANKA-0821400, PBANKA-0417600, PBANKA-0204500, PBANKA-1342300, PBANKA-0417200, PBANKA-0605800, PBANKA-0704900 
       PBANKA-0517600, PBANKA-1231300, PBANKA-1329900, PBANKA-1415200, PBANKA-1115000, PBANKA-1134900, PBANKA-1035200, PBANKA-1233600, PBANKA-0907100, PBANKA-0912500 
PC_ 3 
Positive:  PBANKA-1400600, PBANKA-1459300, PBANKA-0416000, PBANKA-0305100, PBANKA-0304800, PBANKA-1443300, PBANKA-1365200, PBANKA-0830200, PBANKA-0938400, PBANKA-1349000 
       PBANKA-0827200, PBANKA-1137800, PBANKA-1437300, PBANKA-1113300, PBANKA-0932200, PBANKA-0506100, PBANKA-1017100, PBANKA-0313800, PBANKA-0408500, PBANKA-1117000 
       PBANKA-0409800, PBANKA-0922500, PBANKA-0519400, PBANKA-0925600, PBANKA-0316600, PBANKA-0915200, PBANKA-0722921, PBANKA-0934300, PBANKA-0941800, PBANKA-1225000 
Negative:  PBANKA-1431500, PBANKA-1431400, PBANKA-1449000, PBANKA-0806800, PBANKA-1108000, PBANKA-0927700, PBANKA-0911000, PBANKA-0828900, PBANKA-0601200, PBANKA-1208800 
       PBANKA-1409600, PBANKA-0830900, PBANKA-0521200, PBANKA-1029400, PBANKA-0102700, PBANKA-0942400, PBANKA-1304500, PBANKA-1316500, PBANKA-1038700, PBANKA-1333100 
       PBANKA-0902400, PBANKA-1109600, PBANKA-1129600, PBANKA-1119800, PBANKA-0822900, PBANKA-0719700, PBANKA-1429100, PBANKA-1038200, PBANKA-0301800, PBANKA-1320100 
PC_ 4 
Positive:  PBANKA-0519000, PBANKA-1349100, PBANKA-0519100, PBANKA-0519300, PBANKA-1344400, PBANKA-0831000, PBANKA-1002400, PBANKA-0713100, PBANKA-0523700, PBANKA-0111000 
       PBANKA-1032100, PBANKA-1014500, PBANKA-0519400, PBANKA-0932000, PBANKA-0804500, PBANKA-1349000, PBANKA-1315700, PBANKA-0501400, PBANKA-1101400, PBANKA-1425900 
       PBANKA-0519200, PBANKA-1210600, PBANKA-0509600, PBANKA-0619700, PBANKA-1327100, PBANKA-0523800, PBANKA-0307500, PBANKA-1035400, PBANKA-1119600, PBANKA-1117200 
Negative:  PBANKA-0107300, PBANKA-0814200, PBANKA-1214300, PBANKA-0713300, PBANKA-0604300, PBANKA-0405200, PBANKA-0932200, PBANKA-0109100, PBANKA-0823400, PBANKA-0938400 
       PBANKA-0416500, PBANKA-1460400, PBANKA-1437300, PBANKA-1302800, PBANKA-1130500, PBANKA-0406500, PBANKA-1448000, PBANKA-0211500, PBANKA-1444100, PBANKA-1450300 
       PBANKA-0710100, PBANKA-1223100, PBANKA-1419100, PBANKA-0313800, PBANKA-0818900, PBANKA-0919600, PBANKA-1235600, PBANKA-1242300, PBANKA-0916200, PBANKA-1203700 
PC_ 5 
Positive:  PBANKA-0501600, PBANKA-1240600, PBANKA-0707700, PBANKA-0112200, PBANKA-1002600, PBANKA-0208900, PBANKA-0812200, PBANKA-1423000, PBANKA-0832800, PBANKA-1409200 
       PBANKA-0307500, PBANKA-0716900, PBANKA-0100400, PBANKA-1002400, PBANKA-0915000, PBANKA-1364400, PBANKA-1119600, PBANKA-1347000, PBANKA-0819600, PBANKA-1319000 
       PBANKA-1212500, PBANKA-0900900, PBANKA-1202000, PBANKA-1400091, PBANKA-1228800, PBANKA-0911700, PBANKA-0311500, PBANKA-1345900, PBANKA-1025300, PBANKA-0519200 
Negative:  PBANKA-1443300, PBANKA-0925600, PBANKA-0306700, PBANKA-0907200, PBANKA-1349000, PBANKA-1106500, PBANKA-1217400, PBANKA-1107600, PBANKA-1319300, PBANKA-0304800 
       PBANKA-0519400, PBANKA-0312500, PBANKA-1313100, PBANKA-0305100, PBANKA-0112100, PBANKA-0611700, PBANKA-1241800, PBANKA-0304400, PBANKA-0833300, PBANKA-0208000 
       PBANKA-1113300, PBANKA-1339300, PBANKA-1438000, PBANKA-0206300, PBANKA-1340500, PBANKA-0207500, PBANKA-0915200, PBANKA-0808000, PBANKA-1020100, PBANKA-1360000 
## elbow plot
ElbowPlot(pb_sex_filtered, ndims = 30, reduction = "pca")


## UMAP calc
pb_sex_filtered <- RunUMAP(pb_sex_filtered, dims = 1:8, seed.use = 300, n.neighbors = 60, min.dist = 0.5, repulsion.strength = 0.05, local.connectivity = 20)
20:39:44 UMAP embedding parameters a = 0.583 b = 1.334
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:39:44 Read 6191 rows and found 8 numeric columns
20:39:44 Using Annoy for neighbor search, n_neighbors = 60
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
20:39:44 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:39:46 Writing NN index file to temp file /var/folders/wj/rztzclxn1t10cl2sk0plbf3r0000gn/T//RtmpXGhNV1/file43376acfbe03
20:39:46 Searching Annoy index using 1 thread, search_k = 6000
20:39:51 Annoy recall = 100%
20:39:53 Commencing smooth kNN distance calibration using 1 thread
20:39:54 6191 smooth knn distance failures
20:39:57 Initializing from normalized Laplacian + noise
20:39:57 Commencing optimization for 500 epochs, with 184004 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
20:40:27 Optimization finished
## UMAP plot
DimPlot(pb_sex_filtered, reduction = "umap", group.by = "ident", label = TRUE)

8. Save and Export

Save environment

## This saves everything in the global environment for easy recall later
#save.image(file = "GCSKO_10X_QC.RData")
#load(file = "GCSKO_10X_QC.RData")

Save object(s)

## save Rdata
#save(pb_30k_sex_filtered, pb_sex_filtered, file = "Part_2_input.Rdata")
#load(file = "Part_2_input.Rdata")

## save RDS
saveRDS(pb_sex_filtered, file = "/Users/Andy/GCSKO/GCSKO_analysis_git/data_to_export/pb_sex_filtered.RDS", compress = FALSE) 
#pb_sex_filtered <- readRDS("pb_sex_filtered.RDS")

## Save Robj
#save(pb_sex_filtered,file="pb_sex_filtered.Robj")

Appendix

Session Info

R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
 [1] stats4    parallel  grid      stats     graphics  grDevices utils    
 [8] datasets  methods   base     

other attached packages:
 [1] SingleCellExperiment_1.10.1 SummarizedExperiment_1.18.2
 [3] DelayedArray_0.14.1         matrixStats_0.56.0         
 [5] GenomicRanges_1.40.0        GenomeInfoDb_1.24.2        
 [7] IRanges_2.22.2              S4Vectors_0.26.1           
 [9] Biobase_2.48.0              BiocGenerics_0.34.0        
[11] KernSmooth_2.23-17          fields_10.3                
[13] maps_3.3.0                  spam_2.5-1                 
[15] dotCall64_1.0-0             mixtools_1.2.0             
[17] scales_1.1.1                knitr_1.29                 
[19] reshape2_1.4.4              Hmisc_4.4-0                
[21] Formula_1.2-3               survival_3.2-3             
[23] lattice_0.20-41             gridExtra_2.3              
[25] dplyr_1.0.0                 patchwork_1.0.1            
[27] ggplot2bdc_0.3.2            cowplot_1.0.0              
[29] ggpubr_0.4.0                ggplot2_3.3.2              
[31] viridis_0.5.1               viridisLite_0.3.0          
[33] Seurat_3.2.0               

loaded via a namespace (and not attached):
  [1] reticulate_1.16        tidyselect_1.1.0       htmlwidgets_1.5.1     
  [4] Rtsne_0.15             devtools_2.3.0         munsell_0.5.0         
  [7] codetools_0.2-16       ica_1.0-2              future_1.18.0         
 [10] miniUI_0.1.1.1         withr_2.2.0            colorspace_1.4-1      
 [13] highr_0.8              rstudioapi_0.11        ROCR_1.0-11           
 [16] ggsignif_0.6.0         tensor_1.5             listenv_0.8.0         
 [19] labeling_0.3           GenomeInfoDbData_1.2.3 polyclip_1.10-0       
 [22] farver_2.0.3           pheatmap_1.0.12        rprojroot_1.3-2       
 [25] vctrs_0.3.2            generics_0.0.2         xfun_0.15             
 [28] R6_2.4.1               rsvd_1.0.3             bitops_1.0-6          
 [31] spatstat.utils_1.17-0  assertthat_0.2.1       promises_1.1.1        
 [34] nnet_7.3-14            gtable_0.3.0           globals_0.12.5        
 [37] processx_3.4.3         goftest_1.2-2          rlang_0.4.7           
 [40] splines_4.0.2          rstatix_0.6.0          lazyeval_0.2.2        
 [43] acepack_1.4.1          hexbin_1.28.1          broom_0.7.0           
 [46] checkmate_2.0.0        yaml_2.2.1             abind_1.4-5           
 [49] crosstalk_1.1.0.1      backports_1.1.8        httpuv_1.5.4          
 [52] tools_4.0.2            usethis_1.6.1          ellipsis_0.3.1        
 [55] RColorBrewer_1.1-2     sessioninfo_1.1.1      ggridges_0.5.2        
 [58] Rcpp_1.0.5             plyr_1.8.6             zlibbioc_1.34.0       
 [61] base64enc_0.1-3        RCurl_1.98-1.2         purrr_0.3.4           
 [64] ps_1.3.3               prettyunits_1.1.1      rpart_4.1-15          
 [67] deldir_0.1-28          pbapply_1.4-2          zoo_1.8-8             
 [70] haven_2.3.1            ggrepel_0.8.2          cluster_2.1.0         
 [73] fs_1.4.2               tinytex_0.25           magrittr_1.5          
 [76] data.table_1.12.8      RSpectra_0.16-0        openxlsx_4.1.5        
 [79] lmtest_0.9-37          RANN_2.6.1             fitdistrplus_1.1-1    
 [82] pkgload_1.1.0          hms_0.5.3              mime_0.9              
 [85] evaluate_0.14          xtable_1.8-4           rio_0.5.16            
 [88] jpeg_0.1-8.1           readxl_1.3.1           testthat_2.3.2        
 [91] compiler_4.0.2         tibble_3.0.3           crayon_1.3.4          
 [94] htmltools_0.5.0        segmented_1.2-0        mgcv_1.8-31           
 [97] later_1.1.0.1          tidyr_1.1.0            MASS_7.3-51.6         
[100] Matrix_1.2-18          car_3.0-8              cli_2.0.2             
[103] igraph_1.2.5           forcats_0.5.0          pkgconfig_2.0.3       
[106] foreign_0.8-80         plotly_4.9.2.1         XVector_0.28.0        
[109] stringr_1.4.0          callr_3.4.3            digest_0.6.25         
[112] sctransform_0.2.1      RcppAnnoy_0.0.16       spatstat.data_1.4-3   
[115] rmarkdown_2.3          cellranger_1.1.0       leiden_0.3.3          
[118] htmlTable_2.0.1        uwot_0.1.8             curl_4.3              
[121] kernlab_0.9-29         shiny_1.5.0            lifecycle_0.2.0       
[124] nlme_3.1-148           jsonlite_1.7.0         carData_3.0-4         
[127] desc_1.2.0             fansi_0.4.1            pillar_1.4.6          
[130] fastmap_1.0.1          httr_1.4.2             pkgbuild_1.1.0        
[133] glue_1.4.1             remotes_2.1.1          zip_2.1.0             
[136] spatstat_1.64-1        png_0.1-7              stringi_1.4.6         
[139] latticeExtra_0.6-29    memoise_1.1.0          irlba_2.3.3           
[142] future.apply_1.6.0     ape_5.4               
LS0tCnN1YnRpdGxlOiAnR2FtZXRvY3l0ZSBEZXZlbG9wbWVudCBpbiA8aT5QbGFzbW9kaXVtIGJlcmdoZWk8L2k+Jwp0aXRsZTogfAogICFbXSgvVXNlcnMvQW5keS9HQ1NLTy9HQ1NLT19hbmFseXNpc19naXQvR0NTS09fbG9nby5qcGcpe3dpZHRoPTMwMHB4fSAgCiAgMTBYIFF1YWxpdHkgQ29udHJvbAphdXRob3I6ICJbQW5kcmV3IFJ1c3NlbGxdKGh0dHBzOi8vYWpjcnVzc2VsbC53aXhzaXRlLmNvbS9teXNpdGUvYWJvdXQpIgppbnN0aXR1dGU6IFdlbGxjb21lIFNhbmdlciBJbnN0aXR1dGUKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJUIgJWQsICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgI3RvY19mbG9hdDogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCioqKgojIDEuIEludHJvZHVjdGlvbiBhbmQgQWltcyB7LnRhYnNldH0KClR3byBkYXRhc2V0cyB3ZXJlIGdlbmVyYXRlZCB1c2luZyB0aGUgMTBYIEdlbm9taWNzIENocm9taXVtIDMnIHNjUk5BLVNlcSBwbGF0Zm9ybToKCmBgYHtyIGludHJvLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdhc2lzJ30KIyMgbG9hZCBrbml0ciB0byBkaXNwbGF5IHRhYmxlCmxpYnJhcnkoa25pdHIpCiMjIG1ha2UgZGF0YWZyYW1lCnNwZWNpZXMgPC0gYygicGIiLCAicGIiKQpleHBlcmltZW50X25hbWUgPC0gYygnc3RyYWlnaHQgYmxlZWQgZXhwZXJpbWVudCcsJzE6MSBtaXggZXhwZXJpbWVudCcpCnJ1bl9udW1iZXIgPC0gYygiMjIyNTIiLCAiMjQyODQiKQpsYW5lX251bWJlciA8LSBjKCI1IiwgIjEgJiAyIikKc2VxdWVuY2VyIDwtIGMoIkhpc2VxIDQwMDAiLCAiSGlzZXEgMjUwMCIpCmFwcHJveGltYXRlX251bWJlcl9vZl9jZWxscyA8LSBjKCIzMCwwMDAiLCAiNSwwMDAiKQplbXBsb3kuZGF0YSA8LSBkYXRhLmZyYW1lKHNwZWNpZXMsIGV4cGVyaW1lbnRfbmFtZSwgcnVuX251bWJlciwgbGFuZV9udW1iZXIsIHNlcXVlbmNlciwgYXBwcm94aW1hdGVfbnVtYmVyX29mX2NlbGxzLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQojIyBwcmludCBkYXRhZnJhbWUKa2FibGUoZW1wbG95LmRhdGEpCmBgYAoKVGhpcyBzY3JpcHQgZGV0YWlscyB0aGUgcXVhbGl0eSBjb250cm9sIG9mIHRoZSAxOjEgbWl4IGV4cGVyaW1lbnQKClRoaXMgZGF0YSBoYXMgYmVlbiBwcm9jZXNzZWQgdXNpbmcgQ2VsbFJhbmdlciBpbnRvIGNvdW50cyB0YWJsZXMuIFRoaXMgaW5pdGlhbCBhbmFseXNpcyBnYXZlIHRoZSBmb2xsb3dpbmcgbWV0cmljczoKCjxiPlBiIDE6MSBtaXggZXhwZXJpbWVudCAocnVuICM6IDI0Mjg0IGxhbmVzIDEgYW5kIDIgKEhpc2VxIDI1MDApKTo8L2I+Cgo8ZGl2IHN0eWxlPSJ3aWR0aDo1MDBweDsgaGVpZ2h0OjQwMHB4Ij4hW10oL1VzZXJzL0FuZHkvR0NTS08vR0NTS09fYW5hbHlzaXNfZ2l0L2RhdGEvMTBYLzI0Mjg0X2NlbGxyYW5nZXJfb3V0cHV0XzEucG5nKTwvZGl2Pgo8ZGl2IHN0eWxlPSJ3aWR0aDo1MDBweDsgaGVpZ2h0OjIwMHB4Ij4hW10oL1VzZXJzL0FuZHkvR0NTS08vR0NTS09fYW5hbHlzaXNfZ2l0L2RhdGEvMTBYLzI0Mjg0X2NlbGxyYW5nZXJfb3V0cHV0XzIucG5nKTwvZGl2PgoKPGI+V2Ugd2lsbCBsb2FkIHRoaXMgZGF0YSBpbiBhbmQgZm9yIGVhY2ggcnVuOjwvYj4KCkEuIERlZmluZSAnY2VsbHMnCgpCLiBGaWx0ZXIgcG9vciBxdWFsaXR5IGNlbGxzIG91dAoKQy4gRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uIGFuZCBDbHVzdGVyaW5nIAoKRC4gUmVtb3ZlIERvdWJsZXRzCgpFLiBQcmVkaWN0IGxpZmUgQ3ljbGUgU3RhZ2UgKFVzaW5nIEJ1bGsgUk5BLVNlcSBDb3JyZWxhdGlvbikKCiMgMi4gUmVhZCBpbiB0aGUgZGF0YSAgey50YWJzZXR9CgojIyMgTG9hZCB0aGUgcmVxdWlyZWQgcGFja2FnZXMKCmBgYHtyIGxvYWQgcGFja2FnZXMsIGVjaG8gPSBGQUxTRX0KIyMgU2V1cmF0IGlzIG5lZWRlZCBmb3IgbW9zdCBvZiB0aGlzIHNjcmlwdAppZihyZXF1aXJlKCJTZXVyYXQiLCBxdWlldGx5ID0gVFJVRSkpewogICAgcHJpbnQoIlNldXJhdCBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBTZXVyYXQiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiU2V1cmF0IikKICAgIGlmKHJlcXVpcmUoU2V1cmF0KSl7CiAgICAgICAgcHJpbnQoIlNldXJhdCBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIFNldXJhdCIpCiAgICB9Cn0KCiMjIGNvd3Bsb3QgaXMgbmVlZGVkIGZvciBwbG90cwppZihyZXF1aXJlKCJjb3dwbG90IikpewogICAgcHJpbnQoImNvd3Bsb3QgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgY293cGxvdCIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJjb3dwbG90IikKICAgIGlmKHJlcXVpcmUoY293cGxvdCkpewogICAgICAgIHByaW50KCJjb3dwbG90IGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgY293cGxvdCIpCiAgICB9Cn0KCiMjIGdyaWRFeHRyYSBpcyBuZWVkZWQgZm9yIGdyaWQgZ3JhcGhpY3MgdG8gcGxvdCBtdWx0aXBsZSBwbG90cyBpbiB0aGUgc2FtZSB2aWV3CmlmKHJlcXVpcmUoImdyaWRFeHRyYSIpKXsKICAgIHByaW50KCJncmlkRXh0cmEgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgZ3JpZEV4dHJhIikKICAgIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIpCiAgICBpZihyZXF1aXJlKGdyaWRFeHRyYSkpewogICAgICAgIHByaW50KCJncmlkRXh0cmEgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBncmlkRXh0cmEiKQogICAgfQp9CgojI2ZvciBncmlkLmFycmFuZ2UgZnVuY3Rpb24gdG8gY2hhbmdlIHNpemUgb2YgdGl0bGUKaWYocmVxdWlyZSgiZ3JpZCIpKXsKICAgIHByaW50KCJncmlkIGlzIGxvYWRlZCBjb3JyZWN0bHkiKQp9IGVsc2UgewogICAgcHJpbnQoInRyeWluZyB0byBpbnN0YWxsIGdyaWQiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JpZCIpCiAgICBpZihyZXF1aXJlKGdyaWQpKXsKICAgICAgICBwcmludCgiZ3JpZCBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIGdyaWQiKQogICAgfQp9CgojIyBmb3IgZG9pbmcgYnVsayBjb3JyZWxhdGlvbiBjYWxjdWxhdGlvbnMKaWYocmVxdWlyZSgiSG1pc2MiKSl7CiAgICBwcmludCgiSG1pc2MgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgSG1pc2MiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiSG1pc2MiKQogICAgaWYocmVxdWlyZShIbWlzYykpewogICAgICAgIHByaW50KCJIbWlzYyBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIEhtaXNjIikKICAgIH0KfQoKIyMgZHBseXIgaXMgbmVlZGVkIHRvIHdvcmsgd2l0aCBkYXRhIGZyYW1lcwppZihyZXF1aXJlKCJkcGx5ciIpKXsKICAgIHByaW50KCJkcGx5ciBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBkcGx5ciIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCiAgICBpZihyZXF1aXJlKGRwbHlyKSl7CiAgICAgICAgcHJpbnQoImRwbHlyIGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgZHBseXIiKQogICAgfQp9CgojIyBzY2FsZXMgaXMgbmVlZGVkIGZvciBicmVhayBmb3JtYXR0aW5nIGZ1bmN0aW9ucyBpbiB0aGUgYmFyY29kZSBwbG90CmlmKHJlcXVpcmUoInNjYWxlcyIpKXsKICAgIHByaW50KCJzY2FsZXMgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgc2NhbGVzIikKICAgIGluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpCiAgICBpZihyZXF1aXJlKHNjYWxlcykpewogICAgICAgIHByaW50KCJzY2FsZXMgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBzY2FsZXMiKQogICAgfQp9CgojIyBnZ3B1YnIgaXMgbmVlZGVkIGZvciBwbG90dGluZwppZihyZXF1aXJlKCJnZ3B1YnIiLCBxdWlldGx5ID0gVFJVRSkpewogICAgcHJpbnQoImdncHViciBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBnZ3B1YnIiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwdWJyIikKICAgIGlmKHJlcXVpcmUoZ2dwdWJyKSl7CiAgICAgICAgcHJpbnQoImdncHViciBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIGdncHViciIpCiAgICB9Cn0KCiMjIHBhdGNod29yayBpcyBuZWVkZWQgZm9yIHBsb3R0aW5nCiMjIFdBUk5JTkchIGNvd3Bsb3Qgb3Zlci1yaWRlcyB0aGlzIGJ5IG1hc2tpbmcgaXQgc28gYmUgY2FyZWZ1bC4KaWYocmVxdWlyZSgicGF0Y2h3b3JrIiwgcXVpZXRseSA9IFRSVUUpKXsKICAgIHByaW50KCJwYXRjaHdvcmsgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgcGF0Y2h3b3JrIikKICAgIGluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIpCiAgICBpZihyZXF1aXJlKHBhdGNod29yaykpewogICAgICAgIHByaW50KCJwYXRjaHdvcmsgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBwYXRjaHdvcmsiKQogICAgfQp9CgojIyBzZXQgdGhlIHNlZWQgZm9yIGJvdGggdGhlIG1peHR1cmUgbW9kZWxzIGFuZCBhbHNvIGZvciB0aGUgc2FtcGxlIGZ1bmN0aW9uIGxhdGVyIG9uOgpzZXQuc2VlZCgtOTI0OTcpCmBgYAoKIyMjIEltcG9ydCBHVEYgZmlsZQoKVGhpcyB3aWxsIGJlIGhlbHBmdWwgbGF0ZXIgb24uIFRoaXMgY29udGFpbnMgYW5ub3RhdGlvbnMgZm9yIGVhY2ggZ2VuZToKYGBge3IgaW1wb3J0IGd0Zn0KIyNJbXBvcnQgZ3RmIGZpbGU6Cmd0ZiA8LSByZWFkLnRhYmxlKCIvVXNlcnMvQW5keS9HQ1NLTy9HQ1NLT19hbmFseXNpc19naXQvZGF0YS9SZWZlcmVuY2UvUGJlcmdoZWkuZ3RmIiwgc2VwPSJcdCIsIGhlYWRlciA9IEZBTFNFKQpoZWFkKGd0ZikKYGBgCgojIyMgUmVhZCBpbiB0aGUgRGF0YQoKYGBge3IgaW1wb3J0IGRhdGF9CiMjIHJlYWQgaW4gMTB4IG91dHB1dCAKdGVueDVrX3Jhd19kYXRhIDwtIFJlYWQxMFgoIi9Vc2Vycy9BbmR5L0dDU0tPL0dDU0tPX2FuYWx5c2lzX2dpdC9kYXRhLzEwWC90ZW54XzI0Mjg0IikKCiMjIENyZWF0ZSBTZXVyYXQgb2JqZWN0CnRlbng1ayA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gdGVueDVrX3Jhd19kYXRhLCBtaW4uY2VsbHMgPSAwLCBtaW4uZmVhdHVyZXMgPSAwLCBwcm9qZWN0ID0gIkdDU0tPIikKCiMjIGFkZCBleHBlcmltZW50IHRvIG1ldGEgZGF0YSBmb3IgbWVyZ2luZyBsYXRlcgp0ZW54NWtAbWV0YS5kYXRhJGV4cGVyaW1lbnQgPC0gInRlbng1ayIKCiMjIGluc3BlY3QKdGVueDVrCmBgYAoKIyAzLiBEZWZpbmluZyBDZWxscyB2cy4gQmFja2dyb3VuZCB7LnRhYnNldH0KClBsb3QgYSBrbmVlIHBsb3QgYW5kIHRoZW4gdXNlIGEgbWl4dHVyZSBtb2RlbCB0byBkZWZpbmUgd2hlcmUgdGhlIGNlbGxzIHZzLiBiYWNrZ3JvdW5kIGxpZQpgYGB7ciBtaXhtb2RlbCBzZXR1cH0KIyMgaW50ZXJlc3RpbmcgcmVmZXJlbmNlIG1hdGVyaWFsIGZvciB0aGlzIHNlY3Rpb24gY2FuIGJlIGZvdW5kIGhlcmU6IGh0dHBzOi8vaGVtYmVyZy1sYWIuZ2l0aHViLmlvL3NjUk5BLnNlcS5jb3Vyc2UvcHJvY2Vzc2luZy1yYXctc2NybmEtc2VxLWRhdGEuaHRtbCAKCiMjIGdldCB0aGUgblVNSXMKdW1pX3Blcl9iYXJjb2RlIDwtIGFzLmRhdGEuZnJhbWUodGVueDVrQG1ldGEuZGF0YSRuQ291bnRfUk5BKQoKIyMgcmVtb3ZlIHplcm9zIGFzIHRoZXNlIGhhdmUgaXNzdWVzIHdoZW4geW91IGxvZyB0aGVtIGFuZCBtYWtlIHRoZSBtb2RlbCBsYXRlcjoKdW1pX3Blcl9iYXJjb2RlIDwtIGFzLmRhdGEuZnJhbWUodW1pX3Blcl9iYXJjb2RlWyEodW1pX3Blcl9iYXJjb2RlJGB0ZW54NWtAbWV0YS5kYXRhJG5Db3VudF9STkFgPT0wKSwgXSkKCiMjIGdldCBhIHJhbmsgZm9yIGVhY2ggYmFyY29kZQpiYXJjb2RlX3JhbmsgPC0gcmFuaygtdW1pX3Blcl9iYXJjb2RlWywxXSkKCiMjIHRoZW4gbWFrZSBpbnRvIGEgbGlzdApsaWJfc2l6ZSA8LSAodW1pX3Blcl9iYXJjb2RlWywxXSkKCiMjIHRoZW4gbG9nIHRoaXMKbG9nX2xpYl9zaXplIDwtIGxvZzEwKHVtaV9wZXJfYmFyY29kZVssMV0pCgojI3Bsb3QKI3Bsb3QoYmFyY29kZV9yYW5rLCBsb2dfbGliX3NpemUsIHhsaW09YygxLDEwMDAwMCkpCgojIyBvcmRlciB0aGUgYmFyY29kZSByYW5rcwpvIDwtIG9yZGVyKGJhcmNvZGVfcmFuaykKCiMjIHJlb3JkZXIgdGhlIGxpYnJhcnkgc2l6ZSwgYmFyY29kZSByYW5rIGJ5IHRoZWlyIHJhbmsKbG9nX2xpYl9zaXplIDwtIGxvZ19saWJfc2l6ZVtvXQpiYXJjb2RlX3JhbmsgPC0gYmFyY29kZV9yYW5rW29dCmxpYl9zaXplIDwtIGxpYl9zaXplW29dCmBgYAoKbWFrZSBhIG1peHR1cmUgbW9kZWwgdG8gZGV0ZXJtaW5lIHRoZSBrbmVlIG9mIHRoZSBwbG90CmBgYHtyIG1peG1vZGVsIGNhbGN9CiMjIHNldCBhIHNlZWQgZm9yIHRoZSBtaXh0dXJlIG1vZGVsCnNldC5zZWVkKC05MjQ5NykKCiMjIG1peHR1cmUgbW9kZWwgY2FsY3VsYXRpb24gCnJlcXVpcmUoIm1peHRvb2xzIikKbWl4IDwtIG5vcm1hbG1peEVNKGxvZ19saWJfc2l6ZSkKCiMjIHBsb3QgcmVzdWx0CnBsb3QobWl4LCB3aGljaD0yLCB4bGFiMj0ibG9nKG1vbCBwZXIgY2VsbCkiKQpgYGAKCkZpbmQgd2hlcmUgdGhlIGRpc3RyaWJ1dGlvbnMgaW50ZXJzZWN0IChpLmUuIHdoZXJlIGNlbGxzIHZzLiBiYWNrZ3JvdW5kIGlzKQpgYGB7ciBjYWxjdWxhdGUgc3BsaXR9CiMjIGlkZW50aWZ5IHdoZXJlIHRoZSBzcGxpdCBiZXR3ZWVuIHRoZSBkaXN0cmlidXRpb25zIGlzCnAxIDwtIGRub3JtKGxvZ19saWJfc2l6ZSwgbWVhbj1taXgkbXVbMV0sIHNkPW1peCRzaWdtYVsxXSkKcDIgPC0gZG5vcm0obG9nX2xpYl9zaXplLCBtZWFuPW1peCRtdVsyXSwgc2Q9bWl4JHNpZ21hWzJdKQppZiAobWl4JG11WzFdIDwgbWl4JG11WzJdKSB7CiAgICBzcGxpdCA8LSBtaW4obG9nX2xpYl9zaXplW3AyID4gcDFdKQp9IGVsc2UgewogICAgc3BsaXQgPC0gbWluKGxvZ19saWJfc2l6ZVtwMSA+IHAyXSkKfQoKIyMgcHJpbnQgc3BsaXQKc3BsaXQKYGBgCgpWaWV3IHRoZSBpbml0aWFsIHJlc3VsdApgYGB7cn0KIyMgbG9nIHRoZSBiYXJjb2RlIHJhbmsKbG9nX2JhcmNvZGVfcmFuayA8LSBsb2cxMChiYXJjb2RlX3JhbmspCgojIyBwbG90CnBsb3QobG9nX2JhcmNvZGVfcmFuaywgbG9nX2xpYl9zaXplLCB4bGltPWMoMSw2KSkKIyMgYWRkIHRoZSBzcGxpdCBhcyBhIGxpbmUgb24gdGhlIHBsb3QKYWJsaW5lKGg9c3BsaXQsIGNvbD0icmVkIikKYGBgCgpGaW5hbCBGaWd1cmVzOgpgYGB7ciBiYXJjb2RlIHBsb3R9CiMjIG1ha2UgdGhlIHJlc3VsdHMgb2YgdGhlIGFib3ZlIGZ1bmN0aW9ucyBpbnRvIGEgZGF0YWZyYW1lCmRmX2JhcmNvZGVzIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoYmFyY29kZV9yYW5rLCBsb2dfbGliX3NpemUsIGxpYl9zaXplKSwgcm93Lm5hbWVzID0gTlVMTCkKCiMjIGFkZCBhIGNvbHVtbiBmb3IgaWYgaXQgaXMgYSBjZWxsIG9yIG5vdApkZl9iYXJjb2RlcyRjZWxsID0gcm93bmFtZXMoZGZfYmFyY29kZXMpICVpbiUgd2hpY2goZGZfYmFyY29kZXMkbG9nX2xpYl9zaXplID4gc3BsaXQpCgojIyBjaGFuZ2UgdmFsdWUgdG8gYSBudW1lcmljCmRmX2JhcmNvZGVzJGNlbGwgPC0gYXMubnVtZXJpYyhkZl9iYXJjb2RlcyRjZWxsKQoKIyMgY2hhbmdlIHRoZSAwIHRvIGEgMiBmb3IgZWFzZSBvZiBoYW5kbGluZwpkZl9iYXJjb2RlcyRjZWxsW2RmX2JhcmNvZGVzJGNlbGw8MV0gPC0gMgoKIyMgcmVuYW1lIHRoZSBudW1lcmljcyBpbnRvIGNlbGxzIG9yIGJhY2tncm91bmQKZGZfYmFyY29kZXMkY2VsbFtkZl9iYXJjb2RlcyRjZWxsID09IDFdIDwtICJDZWxscyIKZGZfYmFyY29kZXMkY2VsbFtkZl9iYXJjb2RlcyRjZWxsID09IDJdIDwtICJCYWNrZ3JvdW5kIgoKIyMgZXh0cmFjdCB0aGUgY3V0b2ZmIGZvciBjZWxscyBkbyB5b3UgY2FuIHBsb3QgdGhlIGxpbmVzCmJvdW5kYXJ5IDwtIGFzLm51bWVyaWMoc3VtKGRmX2JhcmNvZGVzJGNlbGwgPT0gIkNlbGxzIikpCnNwbGl0IDwtIDEwXnNwbGl0CgojIyBtYWtlIHRoZSBwbG90CmJhcmNvZGVfcGxvdCA8LSBnZ3Bsb3QoZGZfYmFyY29kZXMsIGFlcyh4PWJhcmNvZGVfcmFuaywgeT1saWJfc2l6ZSwgY29sb3VyID0gY2VsbCwgdGhlbWVfc2l6ZSA9IDQwKSkgKwogICMjIG1ha2UgaW50byBhIGRvdCBwbG90CiAgZ2VvbV9wb2ludChzaXplID0gMSwgc2hhcGUgPSAxNikgKwogICMjIG1ha2UgdGhlIGF4aXMgaW50byBsb2cgYW5kIHNwZWNpZnkgYnJlYWtzCiAgc2NhbGVfeF9sb2cxMChicmVha3MgPSB0cmFuc19icmVha3MoImxvZzEwIiwgZnVuY3Rpb24oeCkgMTBeeCksIGxhYmVscyA9IHRyYW5zX2Zvcm1hdCgibG9nMTAiLCBtYXRoX2Zvcm1hdCgxMF4ueCkpKSArCiAgIyMgbWFrZSB0aGUgYXhpcyBpbnRvIGxvZyBhbmQgc3BlY2lmeSBicmVha3MKICBzY2FsZV95X2xvZzEwKGJyZWFrcyA9IHRyYW5zX2JyZWFrcygibG9nMTAiLCBmdW5jdGlvbih4KSAxMF54KSwgbGFiZWxzID0gdHJhbnNfZm9ybWF0KCJsb2cxMCIsIG1hdGhfZm9ybWF0KDEwXi54KSkpICsKICBhbm5vdGF0aW9uX2xvZ3RpY2tzKCkgKwogICMjIGNoYW5nZSBjb2xvdXJzIG9mIHBsb3QKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNiZGJkYmQiLCAiIzViYTQzYSIpLCBsYWJlbHMgPSBjKCJCYWNrZ3JvdW5kIiwgIkNlbGxzIikpICsKICAjIyBjaGFuZ2UgYWVzIG9mIGxlZ2VuZAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIiwgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTI1KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MjUpLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MjUpKSArCiAgIyMgYWRkIHRoZSBsaW5lcyBvbiB0aGUgcGxvdAogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSBzcGxpdCwgeGVuZCA9IGJvdW5kYXJ5LCB5ZW5kID0gc3BsaXQpLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYSA9IDAuMDEpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBib3VuZGFyeSwgeSA9IDAsIHhlbmQgPSBib3VuZGFyeSwgeWVuZCA9IHNwbGl0KSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGEgPSAwLjAxKSArCiAgIyMgY2hhbmdlIHRoZSBheGlzIGxhYmVscwogIGxhYnMoeCA9ICJCYXJjb2RlcyIsIHkgPSAiVU1JIENvdW50cyIsIGNvbG91cj0iQ2VsbCBEZXNpZ25hdGlvbiIpICsKICAjIyBjaGFuZ2UgdGhlIHNpemUgb2YgdGhlIGxlZ2VuZAogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTEwKSkpICsKICAjIyBmaXggYXhpcwogIGNvb3JkX2ZpeGVkKCkgKwogICMjIG1ha2UgaXQgbG9vayBwcmV0dHkKICB0aGVtZV9saWdodCgpCgojIyBwcmludCB0aGUgcGxvdApiYXJjb2RlX3Bsb3QKCiMjIHNhdmUgdGhlIHBsb3QKZ2dzYXZlKCJRQ18xMFhfYmFyY29kZV9wbG90XzVrLnBuZyIsIHBsb3QgPSBiYXJjb2RlX3Bsb3QsIGRldmljZSA9ICJwbmciLCBoZWlnaHQgPSAxNSwgd2lkdGggPSAxNSwgdW5pdHMgPSAiY20iLCBwYXRoID0gIi9Vc2Vycy9BbmR5L0dDU0tPL0dDU0tPX2FuYWx5c2lzX2dpdC9pbWFnZXNfdG9fZXhwb3J0LyIpCmBgYAoKc28gdGhlIG51bWJlciBvZiBjZWxscyB0aGF0IGlzIHJldGFpbmVkIGlzOgpgYGB7ciwgZWNobyA9IEZBTFNFfQp0YWJsZShkZl9iYXJjb2RlcyRjZWxsKQpgYGAKCiMjIyBGaWx0ZXIgdGhlIG9iamVjdAoKYGBge3IgZmlsdGVyIGNlbGxzIHZzIGJhY2tncm91bmR9CiMjIGV4dHJhY3QgdGhlIG5Db3VudCBhbmQgcm93IG5hbWVzIGZyb20gdGhlIFNldXJhdCBvYmplY3QKdXBiIDwtIGRhdGEuZnJhbWUobkNvdW50X1JOQSA9IHRlbng1a0BtZXRhLmRhdGEkbkNvdW50X1JOQSwgcm93Lm5hbWVzID0gcm93bmFtZXModGVueDVrQG1ldGEuZGF0YSkpCgojIyBtYWtlIGJsYW5rIGNvbHVtbiBmb3IgcmFuawp1cGIkcmFuayA8LSBOQQoKIyMgb3JkZXIgYnkgbkNvdW50cwpvcmRlci5zY29yZXMgPC0gb3JkZXIodXBiJG5Db3VudF9STkEsIGRlY3JlYXNpbmcgPSBUUlVFKQoKIyMgYWRkIGEgcmFuayB0byB0aGlzIGNvbHVtbiBiYXNlZCBvbiB0aGUgb3JkZXJlZCBuQ291bnQKdXBiJHJhbmtbb3JkZXIuc2NvcmVzXSA8LSAxOm5yb3codXBiKQoKIyMgaW5zcGVjdAojaGVhZCh1cGIpCgojIyBtYWtlIGEgbGlzdCBvZiBjZWxscyB0byByZXRhaW4gaW4gdGhlIFNldXJhdCBvYmplY3QKa2VlcF9jZWxscyA8LSByb3duYW1lcyh1cGJbd2hpY2godXBiJHJhbmsgPCA3NzYzKSxdKQoKIyMgc3Vic2V0IFNldXJhdCBvYmplY3QgdG8gaW5jbHVkZSBjZWxscyBhbmQgZGlzY2FyZCBiYWNrZ3JvdW5kCnBiX3NleCA8LSBzdWJzZXQodGVueDVrLCBjZWxscyA9IGtlZXBfY2VsbHMpCmBgYAoKIyA0LiBGaWx0ZXIgT3V0IFBvb3ItUXVhbGl0eSBDZWxscyB7LnRhYnNldH0KCiMjIyBGaWx0ZXIgTWl0b2Nob25kcmlhbCAlCgpNaXRvY2hvbmRyaWFsIGNlbGwgY291bnRzCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMTUsIGZpZy53aWR0aCA9IDd9CiMjIGV4dHJhY3QgbWl0b2Nob25kcmlhbCBnZW5lcyAKI21pdG9fZ2VuZXMgPC0gZ3RmW3doaWNoKGd0ZiRWMyA9PSAiclJOQSIpLF0kVjkKI21pdG9fZ2VuZXMgPC0gZ3N1YigiOy4qIiwiIiwgZ3N1YigiZ2VuZV9pZCAiLCAiIiwgbWl0b19nZW5lcykpCiNwYXN0ZSgiVGhlc2UgYXJlIHRoZSBtaXRvY2hvbmRyaWFsIGdlbmVzIikKI2hlYWQobWl0b19nZW5lcykKCiMjIGV4dHJhY3QgbWl0byBnZW5lcwptaXRvX2dlbmVzIDwtIHBiX3NleEBhc3NheXMkUk5BQGNvdW50c0BEaW1uYW1lc1tbMV1dW2dyZXAoIl5QQkFOS0EtTUlUIixwYl9zZXhAYXNzYXlzJFJOQUBjb3VudHNARGltbmFtZXNbWzFdXSldCgojIyBwbG90IHRoZSBnZW5lcyBpbmRpdmlkdWFsbHkKVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gbWl0b19nZW5lcywgcHQuc2l6ZSA9IDAuMDEpCgojIyBtYWtlIGEgcGVyY2VudGFnZSBtaXRvY29uZHJpYWwgZm9yIGVhY2ggY2VsbCAodGhpcyB3aWxsIHdvcmsgYXMgbG9uZyBhcyB5b3UgZmlsdGVyIGNlbGxzIG91dCB3aXRoIHplcm8gY291bnRzKQpwYl9zZXggPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQocGJfc2V4LCBwYXR0ZXJuID0gIl5QQkFOS0EtTUlUIiwgY29sLm5hbWUgPSAicGVyY2VudC5tdCIpCmBgYAoKcGxvdCBwZXJjZW50YWdlIG1pdG9jaG9uZHJpYWwKYGBge3J9CiMjIHBsb3QgZm9yIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCByZWFkcwp2MSA8LSBWbG5QbG90KG9iamVjdCA9IHBiX3NleCwgZmVhdHVyZXMgPSAicGVyY2VudC5tdCIsIHB0LnNpemUgPSAwLjAxKSArCiAgIyMgYWRkIGEgbGluZSB3aGVyZSB3ZSB3aWxsIGZpbHRlcgogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDIwLCBjb2w9ImJsdWUiKSArCiAgIyMgY2hhbmdlIGxhYmVscwogIGxhYnMoeCA9ICIiLHkgPSAiJSBNaXRvY2hvbmRyaWFsIFJlYWRzIiwgdGl0bGUgPSAiTWl0b2Nob25kcmlhbCBwZXIgY2VsbCIpICsKICAjIyByZW1vdmUgbGVnZW5kCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgIyMgY2hhbmdlIGFwcGVhcmFuY2UKICB0aGVtZV9jbGFzc2ljKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0iZ3JleSIpICsKICAjc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTAwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLCB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0yMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0yMCksIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChjb2xvdXI9ImJsYWNrIikpCgojIyBwcmludAp2MQoKIyMgc2F2ZQojZ2dzYXZlKCJ+L2ltYWdlc190b19leHBvcnQvUUNfMTBYX21pdG9fdmlvbGluLnBuZyIsIHBsb3QgPSBRQ19taXRvX3Zpb2xpbiwgZGV2aWNlID0gInBuZyIsIHBhdGggPSBOVUxMLCBzY2FsZSA9IDEsIHdpZHRoID0gMTUsIGhlaWdodCA9IDEwLCB1bml0cyA9ICJjbSIsIGRwaSA9IDMwMCwgbGltaXRzaXplID0gVFJVRSkKYGBgCgpJbiB0aGUgU21hcnQtc2VxMiBkYXRhLCB3ZSB1c2UgYSB0aHJlc2hvbGQgb2YgMjAlLiBObyBjZWxsIGluIG91ciAxMFggZGF0YSBpcyBoaWdoZXIgdGhhbiAxMyUgYW5kIG9ubHkKYGBge3IsIGVjaG8gPSBGQUxTRX0Kc3VtKHBiX3NleEBtZXRhLmRhdGEkcGVyY2VudC5tdCA+IDUpCmBgYApjZWxscyBoYXZlIGEgJSBtaXRvY2hvbmRyaWFsIHJlYWRzIGFib3ZlIDUlLiAKCiMjIyBuR2VuZXMgZmlsdGVyCgpwbG90IGluZGl2aWR1YWwgdmlvbGluIHBsb3RzCmBgYHtyfQojIyBuR2VuZXMgcGxvdApnZW5lX3Bsb3RfNWsgPC0gVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIHB0LnNpemUgPSAwLjAxKQoKIyMgaW1wcm92ZSB0aGUgYWVzdGhldGljcwpnZW5lX3Bsb3RfNWsgPC0gZ2VuZV9wbG90XzVrICsgCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMjAwLCBjb2w9ImJsdWUiKSArCiAgbGFicyh4ID0gIiIseSA9ICJuR2VuZSIsIHRpdGxlID0gIkdlbmVzIHBlciBjZWxsIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMwMDApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siKSkKCiMjIG5VTUkgcGxvdApudW1pX3Bsb3RfNWsgPC0gVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gIm5Db3VudF9STkEiLCBwdC5zaXplID0gMC4wMSkKCiMjIGltcHJvdmUgYWVzdGhldGljcwpudW1pX3Bsb3RfNWsgPC0gbnVtaV9wbG90XzVrICsKICBsYWJzKHggPSAiIix5ID0gIm5VTUkiLCB0aXRsZSA9ICJVTUlzIHBlciBjZWxsIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMwMDApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siKSkKCiMjIHBsb3QgdG9nZXRoZXIKZ3JpZC5hcnJhbmdlKGdlbmVfcGxvdF81aywgbnVtaV9wbG90XzVrLCBuY29sID0gMiwgdG9wPXRleHRHcm9iKCI1SyBjZWxscyAxMFgiLCBncD1ncGFyKGZvbnRzaXplPTE1LGZvbnQ9OCkpKQoKIyMgc2F2ZSBuR2VuZSBwbG90IG9uIGl0cyBvd24KI2dnc2F2ZSgiUUNfbmdlbmVfcGxvdC5wZGYiLCBwbG90ID0gZ2VuZTEsIGRldmljZSA9ICJwZGYiLCBoZWlnaHQgPSA1LCB3aWR0aCA9IDUsIHVuaXRzID0gImluIiwgcGF0aCA9ICIvVXNlcnMvYXIxOS9EZXNrdG9wL1BoRC9HQ1NLT19BbmFseXNpcyIpCmBgYAoKcGxvdCB0d28gbWV0cmljcyB0b2dldGhlcgpgYGB7ciwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDh9CiMjIG1ha2UgYSBkYXRhZnJhbWUgZm9yIGltcG9ydGFudCBmaWx0ZXJpbmcgbWV0cmljcwpkZiA8LSBkYXRhLmZyYW1lKG5Db3VudCA9IGxvZzEwKHBiX3NleEBtZXRhLmRhdGEkbkNvdW50X1JOQSksIG5HZW5lcyA9IHBiX3NleEBtZXRhLmRhdGEkbkZlYXR1cmVfUk5BLCBwZXJjZW50X210ID0gcGJfc2V4QG1ldGEuZGF0YSRwZXJjZW50Lm10LCBleHBlcmltZW50ID0gcGJfc2V4QG1ldGEuZGF0YSRleHBlcmltZW50KQoKIyMgcGxvdCBtYWluIGRvdHBsb3QKcGxvdDEgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IG5Db3VudCwgeSA9IG5HZW5lcykpICsKICAjZ2VvbV9wb2ludChhZXMoKSwgc2l6ZSA9IDAuMSkgKwogIGdlb21faGV4KGJpbnMgPSAyMDApICsKICAjZ2VvbV9ydWcoKSArIAogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIk51bWJlciBvZiBEZXRlY3RlZCBHZW5lcyIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAibG9nMTAoTnVtYmVyIG9mIFRvdGFsIFVNSSkiKSArIAogIHRoZW1lX3B1YnIoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MjAwKQoKIyMgcGxvdCBkZW5zaXR5IHBsb3QgeApkZW5zIDwtIGRlbnNpdHkoZGYkbkNvdW50KQpkdCA8LSBkYXRhLmZyYW1lKHg9ZGVucyR4LCB5PWRlbnMkeSkKcHJvYnMgPC0gYygwLCAwLjI1LCAwLjUsIDAuNzUsIDEpCnF1YW50aWxlcyA8LSBxdWFudGlsZShkZiRuQ291bnQsIHByb2I9cHJvYnMpCmR0JHF1YW50IDwtIGZhY3RvcihmaW5kSW50ZXJ2YWwoZHQkeCxxdWFudGlsZXMpKQpkZW5zMSA8LSBnZ3Bsb3QoZHQsIGFlcyh4LHkpKSArIGdlb21fbGluZSgpICsgZ2VvbV9yaWJib24oYWVzKHltaW49MCwgeW1heD15LCBmaWxsPXF1YW50KSkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXF1YW50aWxlcykgKyBzY2FsZV9maWxsX2JyZXdlcihndWlkZT0ibm9uZSIpICsgdGhlbWVfdm9pZCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQojIyBwbG90IGRlbnNpdHkgcGxvdCB5CiMjIG9sZCBtZXRob2QKIyBkZW5zMiA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gbkdlbmVzLCB5ID0gZXhwZXJpbWVudCkpICsKIyAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMikgKwojICAgdGhlbWVfdm9pZCgpICsKIyAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwojICAgY29vcmRfZmxpcCgpCiMjIG5ldyBtZXRob2QKZGVucyA8LSBkZW5zaXR5KGRmJG5HZW5lcykKZHQgPC0gZGF0YS5mcmFtZSh4PWRlbnMkeCwgeT1kZW5zJHkpCnByb2JzIDwtIGMoMCwgMC4yNSwgMC41LCAwLjc1LCAxKQpxdWFudGlsZXMgPC0gcXVhbnRpbGUoZGYkbkdlbmVzLCBwcm9iPXByb2JzKQpkdCRxdWFudCA8LSBmYWN0b3IoZmluZEludGVydmFsKGR0JHgscXVhbnRpbGVzKSkKZGVuczIgPC0gZ2dwbG90KGR0LCBhZXMoeCx5KSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcmliYm9uKGFlcyh5bWluPTAsIHltYXg9eSwgZmlsbD1xdWFudCkpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1xdWFudGlsZXMpICsgc2NhbGVfZmlsbF9icmV3ZXIoZ3VpZGU9Im5vbmUiKSArIHRoZW1lX3ZvaWQoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGNvb3JkX2ZsaXAoKQoKIyMgcGxvdCB0b2dldGhlcgpRQ19jb21wb3NpdGVfcGxvdCA8LSBkZW5zMSArIHBsb3Rfc3BhY2VyKCkgKyBwbG90MSArIGRlbnMyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIsIG5yb3cgPSAyLCB3aWR0aHMgPSBjKDQsIDEpLCBoZWlnaHRzID0gYygxLCA0KSkKCiMjIHByaW50ClFDX2NvbXBvc2l0ZV9wbG90CgojIyBzYXZlCmdnc2F2ZSgiL1VzZXJzL0FuZHkvR0NTS08vR0NTS09fYW5hbHlzaXNfZ2l0L2ltYWdlc190b19leHBvcnQvUUNfMTBYX2NvbXBvc2l0ZV9wbG90LnBuZyIsIHBsb3QgPSBRQ19jb21wb3NpdGVfcGxvdCwgZGV2aWNlID0gInBuZyIsIHBhdGggPSBOVUxMLCBzY2FsZSA9IDEsIHdpZHRoID0gMTUsIGhlaWdodCA9IDE1LCB1bml0cyA9ICJjbSIsIGRwaSA9IDMwMCwgbGltaXRzaXplID0gVFJVRSkKYGBgCgojIyMgRmlsdGVyaW5nCgpUaGUgdGhyZXNob2xkIHVzZWQgaW4gdGhlIG1hbGFyaWEgY2VsbCBhdGxhcyB3YXMgMjMwIGZvciBQYiBidXQgdGhpcyBpcyBkZXBlbmRlbnQgb24gc2VxdWVuY2luZyBkZXB0aCBldGMuCldlIGNhbiBwbG90IHRoZSBudW1iZXIgb2YgY2VsbHMgcmVjb3ZlcmVkIGZvciBhIHJhbmdlIG9mIHRocmVzaG9sZHM6CmBgYHtyfQpwYXN0ZSgib3JpZ2luYWwgbnVtYmVyIG9mIGNlbGxzID0iLCBucm93KHBiX3NleEBtZXRhLmRhdGEpKQpwYXN0ZSgid2l0aCA+MTUwIGZpbHRlciA9IiwgbnJvdyhwYl9zZXhAbWV0YS5kYXRhW3BiX3NleEBtZXRhLmRhdGEkbkZlYXR1cmVfUk5BID4gMTUwLCBdKSkKcGFzdGUoIndpdGggPjIwMCBmaWx0ZXIgPSIsIG5yb3cocGJfc2V4QG1ldGEuZGF0YVtwYl9zZXhAbWV0YS5kYXRhJG5GZWF0dXJlX1JOQSA+IDIwMCwgXSkpCnBhc3RlKCJ3aXRoID4yMzAgZmlsdGVyID0iLCBucm93KHBiX3NleEBtZXRhLmRhdGFbcGJfc2V4QG1ldGEuZGF0YSRuRmVhdHVyZV9STkEgPiAyMzAsIF0pKQpgYGAKClNpbmNlIHdlIGhhdmUgYWxyZWFkeSBmaWx0ZXJlZCBvbiBuVU1JLCB3ZSB3aWxsIGZpbHRlciB3aXRoIDIwMC4KCmBgYHtyfQojIyBudW1iZXIgb2YgY2VsbHMgYmVmb3JlIGZpbHRlcmluZwpwYl9zZXhfcHJlX2ZpbHRlcl9uQ2VsbHMgPC0gbnJvdyhwYl9zZXhAbWV0YS5kYXRhKQojIyBmaWx0ZXIgb2JqZWN0CnBiX3NleCA8LSBzdWJzZXQocGJfc2V4LCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiAyMDApCiMjIG51bWJlciBvZiBjZWxscyBhZnRlciBmaWx0ZXJpbmcKcGJfc2V4X3Bvc3RfZmlsdGVyX25DZWxscyA8LSBucm93KHBiX3NleEBtZXRhLmRhdGEpCiMjIHByaW50IHJlc3VsdHMgb2YgZmlsdGVyaW5nCnBhc3RlKCJudW1iZXIgb2YgY2VsbHMgcHJlLWZpbHRlciIsIHBiX3NleF9wcmVfZmlsdGVyX25DZWxscykKcGFzdGUoIm51bWJlciBvZiBjZWxscyBwb3N0LWZpbHRlciIsIHBiX3NleF9wb3N0X2ZpbHRlcl9uQ2VsbHMpCnBhc3RlKChwYl9zZXhfcHJlX2ZpbHRlcl9uQ2VsbHMgLSBwYl9zZXhfcG9zdF9maWx0ZXJfbkNlbGxzKSwgImNlbGxzIHdlcmUgcmVtb3ZlZCBieSBmaWx0ZXJpbmcgb24gbnVtYmVyIG9mIGdlbmVzLiIpCmBgYAoKIyA1LiBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24gYW5kIENsdXN0ZXJpbmcgey50YWJzZXR9CgojIyMgUHJlcGFyZSBkYXRhCmBgYHtyfQojIyBub3JtYWxpc2Ugb2JqZWN0CnBiX3NleCA8LSBOb3JtYWxpemVEYXRhKHBiX3NleCwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCgojIyBmaW5kIHZhcmlhYmxlIGdlbmVzCnBiX3NleCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhwYl9zZXgsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgbmZlYXR1cmVzID0gMjAwMCkKCiMjIHNjYWxlIHRoZSBkYXRhCmFsbC5nZW5lcyA8LSByb3duYW1lcyhwYl9zZXgpCnBiX3NleCA8LSBTY2FsZURhdGEocGJfc2V4LCBmZWF0dXJlcyA9IGFsbC5nZW5lcykKYGBgCgojIyMgUENBCmBgYHtyfQojIyBydW4gUENBCnBiX3NleCA8LSBSdW5QQ0EocGJfc2V4LCBmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gcGJfc2V4KSkKCiMjIHBsb3QgCkRpbVBsb3QocGJfc2V4LCByZWR1Y3Rpb24gPSAicGNhIikKCiMjIGVsYm93IHBsb3QKRWxib3dQbG90KHBiX3NleCwgbmRpbXMgPSAzMCwgcmVkdWN0aW9uID0gInBjYSIpCmBgYAoKIyMjIFVNQVAKYGBge3J9CiMjIHJ1biBVTUFQCnBiX3NleCA8LSBSdW5VTUFQKHBiX3NleCwgZGltcyA9IDE6MTAsIHNlZWQudXNlID0gMTIzNCwgbi5uZWlnaGJvcnMgPSAxNTApCgojIyBwbG90CkRpbVBsb3QocGJfc2V4LCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImlkZW50IiwgbGFiZWwgPSBUUlVFKQoKIyMgVGhlc2UgYXJlIHRoZSBwYXJhbWV0ZXJzIHVzZWQgaW4gdGhlIG1lcmdlIFVNQVAKI3BiX3NleCA8LSBSdW5VTUFQKHBiX3NleCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjEwLCBuLm5laWdoYm9ycyA9IDE1MCwgc2VlZC51c2UgPSAxMjM0LCBtaW4uZGlzdCA9IDAuNCwgcmVwdWxzaW9uLnN0cmVuZ3RoID0gMC4wMywgbG9jYWwuY29ubmVjdGl2aXR5ID0gMTUwKQojRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbCA9IFRSVUUpCmBgYAoKY29sb3VyIHdpdGggYSBmZXcgbWFya2VyIGdlbmVzOgpgYGB7ciwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDh9CiMgUEJBTktBLTA1MTUwMDAgLSBwMjUgLSBmZW1hbGUKIyBQQkFOS0EtMTIxMjYwMCAtIEhBUDIgLSBtYWxlCiMgUEJBTktBLTA2MDA2MDAgLSBORUszIC0gbWFsZQojIFBCQU5LQS0wODMxMDAwIC0gTVNQMSAtIGxhdGUgYXNleHVhbAojIFBCQU5LQS0xMzE1NzAwIC0gUk9OMiAtIChhc2V4dWFscyBhbmQgc29tZSBtYWxlPykKIyBQQkFOS0EtMDQxNjEwMCAtIGR5bmVuaW4gaGVhdnkgY2hhaW4gLSBtYWxlIC0gdXNlZCBpbiA4MjAgbGluZQojIFBCQU5LQS0xMzE5NTAwIC0gQ0NQMiAtIGZlbWFsZSAtIHVzZWQgaW4gODIwIGxpbmUgCiMgUEJBTktBLTE0Mzc1MDAgLSBBUDItRyAtIHNldXhhbCBjb21taXRtZW50IGdlbmUKIyBQQkFOS0EtMTEwMjIwMCAtIE1TUDggLSBlYXJseSBhc2V4dWFsIChmcm9tIEJvemRlY2ggcGFwZXIpCgpGZWF0dXJlUGxvdChwYl9zZXgsIGZlYXR1cmVzID0gYygiUEJBTktBLTA1MTUwMDAiLCAiUEJBTktBLTEyMTI2MDAiLCJQQkFOS0EtMDYwMDYwMCIsICJQQkFOS0EtMDgzMTAwMCIsICJQQkFOS0EtMTMxNTcwMCIsICJQQkFOS0EtMDQxNjEwMCIsICJQQkFOS0EtMTMxOTUwMCIsICJQQkFOS0EtMTQzNzUwMCIsICJQQkFOS0EtMTEwMjIwMCIpKQpgYGAKCiMjIyBDbHVzdGVyaW5nCmBgYHtyfQpwYl9zZXggPC0gRmluZE5laWdoYm9ycyhwYl9zZXgsIGRpbXMgPSAxOjIxKQpwYl9zZXggPC0gRmluZENsdXN0ZXJzKHBiX3NleCwgcmVzb2x1dGlvbiA9IDEpCmBgYAoKIyA2LiBSZW1vdmUgRG91YmxldHMgey50YWJzZXR9CgojIyMgRG91YmxldEZpbmRlcgoKRG91YmxldEZpbmRlciBmdW5jdGlvbgpgYGB7cn0KIyMgRG91YmxldEZpbmRlciBzaG91bGQgYmUgYWJsZSB0byBiZSBpbnN0YWxsZWQgYW5kIHJ1biBhcyBzbzoKI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YignY2hyaXMtbWNnaW5uaXMtdWNzZi9Eb3VibGV0RmluZGVyJykKI2xpYnJhcnkoZG91YmxldEZpbmRlcikgI2FsbG93cyByZW1vdmFsIG9mIGRvdWJsZXRzCiMjIGJ1dCB0aGVyZSBzZWVtcyB0byBiZSBzb21lIHByb2JsZW1zIHdpdGggdGhpcyBzbyB3ZSB3aWxsIHJ1biBpdCBmcm9tIHRoZSBnaXRodWIgY29kZQoKIyMgdGhlIGRvdWJsZXQgZmluZGVyIGZ1bmN0aW9uCmRvdWJsZXRGaW5kZXJfdjMgPC0gZnVuY3Rpb24oc2V1LCBQQ3MsIHBOID0gMC4yNSwgcEssIG5FeHAsIHJldXNlLnBBTk4gPSBGQUxTRSwgc2N0ID0gRkFMU0UpIHsKICByZXF1aXJlKFNldXJhdCk7IHJlcXVpcmUoZmllbGRzKTsgcmVxdWlyZShLZXJuU21vb3RoKQoKICAjIyBHZW5lcmF0ZSBuZXcgbGlzdCBvZiBkb3VibGV0IGNsYXNzaWZpY2F0b25zIGZyb20gZXhpc3RpbmcgcEFOTiB2ZWN0b3IgdG8gc2F2ZSB0aW1lCiAgaWYgKHJldXNlLnBBTk4gIT0gRkFMU0UgKSB7CiAgICBwQU5OLm9sZCA8LSBzZXVAbWV0YS5kYXRhWyAsIHJldXNlLnBBTk5dCiAgICBjbGFzc2lmaWNhdGlvbnMgPC0gcmVwKCJTaW5nbGV0IiwgbGVuZ3RoKHBBTk4ub2xkKSkKICAgIGNsYXNzaWZpY2F0aW9uc1tvcmRlcihwQU5OLm9sZCwgZGVjcmVhc2luZz1UUlVFKVsxOm5FeHBdXSA8LSAiRG91YmxldCIKICAgIHNldUBtZXRhLmRhdGFbLCBwYXN0ZSgiREYuY2xhc3NpZmljYXRpb25zIixwTixwSyxuRXhwLHNlcD0iXyIpXSA8LSBjbGFzc2lmaWNhdGlvbnMKICAgIHJldHVybihzZXUpCiAgfQoKICBpZiAocmV1c2UucEFOTiA9PSBGQUxTRSkgewogICAgIyMgTWFrZSBtZXJnZWQgcmVhbC1hcnRpZmljYWwgZGF0YQogICAgcmVhbC5jZWxscyA8LSByb3duYW1lcyhzZXVAbWV0YS5kYXRhKQogICAgZGF0YSA8LSBzZXVAYXNzYXlzJFJOQUBjb3VudHNbLCByZWFsLmNlbGxzXQogICAgbl9yZWFsLmNlbGxzIDwtIGxlbmd0aChyZWFsLmNlbGxzKQogICAgbl9kb3VibGV0cyA8LSByb3VuZChuX3JlYWwuY2VsbHMvKDEgLSBwTikgLSBuX3JlYWwuY2VsbHMpCiAgICBwcmludChwYXN0ZSgiQ3JlYXRpbmciLG5fZG91YmxldHMsImFydGlmaWNpYWwgZG91YmxldHMuLi4iLHNlcD0iICIpKQogICAgcmVhbC5jZWxsczEgPC0gc2FtcGxlKHJlYWwuY2VsbHMsIG5fZG91YmxldHMsIHJlcGxhY2UgPSBUUlVFKQogICAgcmVhbC5jZWxsczIgPC0gc2FtcGxlKHJlYWwuY2VsbHMsIG5fZG91YmxldHMsIHJlcGxhY2UgPSBUUlVFKQogICAgZG91YmxldHMgPC0gKGRhdGFbLCByZWFsLmNlbGxzMV0gKyBkYXRhWywgcmVhbC5jZWxsczJdKS8yCiAgICBjb2xuYW1lcyhkb3VibGV0cykgPC0gcGFzdGUoIlgiLCAxOm5fZG91YmxldHMsIHNlcCA9ICIiKQogICAgZGF0YV93ZG91YmxldHMgPC0gY2JpbmQoZGF0YSwgZG91YmxldHMpCgogICAgIyMgU3RvcmUgaW1wb3J0YW50IHByZS1wcm9jZXNzaW5nIGluZm9ybWF0aW9uCiAgICBvcmlnLmNvbW1hbmRzIDwtIHNldUBjb21tYW5kcwoKICAgICMjIFByZS1wcm9jZXNzIFNldXJhdCBvYmplY3QKICAgIGlmIChzY3QgPT0gRkFMU0UpIHsKICAgICAgcHJpbnQoIkNyZWF0aW5nIFNldXJhdCBvYmplY3QuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBkYXRhX3dkb3VibGV0cykKCiAgICAgIHByaW50KCJOb3JtYWxpemluZyBTZXVyYXQgb2JqZWN0Li4uIikKICAgICAgc2V1X3dkb3VibGV0cyA8LSBOb3JtYWxpemVEYXRhKHNldV93ZG91YmxldHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9IG9yaWcuY29tbWFuZHMkTm9ybWFsaXplRGF0YS5STkFAcGFyYW1zJG5vcm1hbGl6YXRpb24ubWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUuZmFjdG9yID0gb3JpZy5jb21tYW5kcyROb3JtYWxpemVEYXRhLlJOQUBwYXJhbXMkc2NhbGUuZmFjdG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gb3JpZy5jb21tYW5kcyROb3JtYWxpemVEYXRhLlJOQUBwYXJhbXMkbWFyZ2luKQoKICAgICAgcHJpbnQoIkZpbmRpbmcgdmFyaWFibGUgZ2VuZXMuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldV93ZG91YmxldHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0aW9uLm1ldGhvZCA9IG9yaWcuY29tbWFuZHMkRmluZFZhcmlhYmxlRmVhdHVyZXMuUk5BJHNlbGVjdGlvbi5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9lc3Muc3BhbiA9IG9yaWcuY29tbWFuZHMkRmluZFZhcmlhYmxlRmVhdHVyZXMuUk5BJGxvZXNzLnNwYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpcC5tYXggPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRjbGlwLm1heCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuLmZ1bmN0aW9uID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbWVhbi5mdW5jdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwZXJzaW9uLmZ1bmN0aW9uID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkZGlzcGVyc2lvbi5mdW5jdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW0uYmluID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbnVtLmJpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5uaW5nLm1ldGhvZCA9IG9yaWcuY29tbWFuZHMkRmluZFZhcmlhYmxlRmVhdHVyZXMuUk5BJGJpbm5pbmcubWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mZWF0dXJlcyA9IG9yaWcuY29tbWFuZHMkRmluZFZhcmlhYmxlRmVhdHVyZXMuUk5BJG5mZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuLmN1dG9mZiA9IG9yaWcuY29tbWFuZHMkRmluZFZhcmlhYmxlRmVhdHVyZXMuUk5BJG1lYW4uY3V0b2ZmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BlcnNpb24uY3V0b2ZmID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkZGlzcGVyc2lvbi5jdXRvZmYpCgogICAgICBwcmludCgiU2NhbGluZyBkYXRhLi4uIikKICAgICAgc2V1X3dkb3VibGV0cyA8LSBTY2FsZURhdGEoc2V1X3dkb3VibGV0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBvcmlnLmNvbW1hbmRzJFNjYWxlRGF0YS5STkEkZmVhdHVyZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsLnVzZSA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRtb2RlbC51c2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLnNjYWxlID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJGRvLnNjYWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkby5jZW50ZXIgPSBvcmlnLmNvbW1hbmRzJFNjYWxlRGF0YS5STkEkZG8uY2VudGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS5tYXggPSBvcmlnLmNvbW1hbmRzJFNjYWxlRGF0YS5STkEkc2NhbGUubWF4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9jay5zaXplID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJGJsb2NrLnNpemUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscy50by5ibG9jayA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRtaW4uY2VsbHMudG8uYmxvY2spCgogICAgICBwcmludCgiUnVubmluZyBQQ0EuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIFJ1blBDQShzZXVfd2RvdWJsZXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRmZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnBjcyA9IGxlbmd0aChQQ3MpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXYucGNhID0gIG9yaWcuY29tbWFuZHMkUnVuUENBLlJOQSRyZXYucGNhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQuYnkudmFyID0gb3JpZy5jb21tYW5kcyRSdW5QQ0EuUk5BJHdlaWdodC5ieS52YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2U9RkFMU0UpCiAgICAgIHBjYS5jb29yZCA8LSBzZXVfd2RvdWJsZXRzQHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5nc1sgLCBQQ3NdCiAgICAgIGNlbGwubmFtZXMgPC0gcm93bmFtZXMoc2V1X3dkb3VibGV0c0BtZXRhLmRhdGEpCiAgICAgIG5DZWxscyA8LSBsZW5ndGgoY2VsbC5uYW1lcykKICAgICAgcm0oc2V1X3dkb3VibGV0cyk7IGdjKCkgIyBGcmVlIHVwIG1lbW9yeQogICAgfQoKICAgIGlmIChzY3QgPT0gVFJVRSkgewogICAgICByZXF1aXJlKHNjdHJhbnNmb3JtKQogICAgICBwcmludCgiQ3JlYXRpbmcgU2V1cmF0IG9iamVjdC4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGRhdGFfd2RvdWJsZXRzKQoKICAgICAgcHJpbnQoIlJ1bm5pbmcgU0NUcmFuc2Zvcm0uLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIFNDVHJhbnNmb3JtKHNldV93ZG91YmxldHMpCgogICAgICBwcmludCgiUnVubmluZyBQQ0EuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIFJ1blBDQShzZXVfd2RvdWJsZXRzLCBucGNzID0gbGVuZ3RoKFBDcykpCiAgICAgIHBjYS5jb29yZCA8LSBzZXVfd2RvdWJsZXRzQHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5nc1sgLCBQQ3NdCiAgICAgIGNlbGwubmFtZXMgPC0gcm93bmFtZXMoc2V1X3dkb3VibGV0c0BtZXRhLmRhdGEpCiAgICAgIG5DZWxscyA8LSBsZW5ndGgoY2VsbC5uYW1lcykKICAgICAgcm0oc2V1X3dkb3VibGV0cyk7IGdjKCkKICAgIH0KCiAgICAjIyBDb21wdXRlIFBDIGRpc3RhbmNlIG1hdHJpeAogICAgcHJpbnQoIkNhbGN1bGF0aW5nIFBDIGRpc3RhbmNlIG1hdHJpeC4uLiIpCiAgICBkaXN0Lm1hdCA8LSBmaWVsZHM6OnJkaXN0KHBjYS5jb29yZCkKCiAgICAjIyBDb21wdXRlIHBBTk4KICAgIHByaW50KCJDb21wdXRpbmcgcEFOTi4uLiIpCiAgICBwQU5OIDwtIGFzLmRhdGEuZnJhbWUobWF0cml4KDBMLCBucm93ID0gbl9yZWFsLmNlbGxzLCBuY29sID0gMSkpCiAgICByb3duYW1lcyhwQU5OKSA8LSByZWFsLmNlbGxzCiAgICBjb2xuYW1lcyhwQU5OKSA8LSAicEFOTiIKICAgIGsgPC0gcm91bmQobkNlbGxzICogcEspCiAgICBmb3IgKGkgaW4gMTpuX3JlYWwuY2VsbHMpIHsKICAgICAgbmVpZ2hib3JzIDwtIG9yZGVyKGRpc3QubWF0WywgaV0pCiAgICAgIG5laWdoYm9ycyA8LSBuZWlnaGJvcnNbMjooayArIDEpXQogICAgICBuZWlnaGJvci5uYW1lcyA8LSByb3duYW1lcyhkaXN0Lm1hdClbbmVpZ2hib3JzXQogICAgICBwQU5OJHBBTk5baV0gPC0gbGVuZ3RoKHdoaWNoKG5laWdoYm9ycyA+IG5fcmVhbC5jZWxscykpL2sKICAgIH0KCiAgICBwcmludCgiQ2xhc3NpZnlpbmcgZG91YmxldHMuLiIpCiAgICBjbGFzc2lmaWNhdGlvbnMgPC0gcmVwKCJTaW5nbGV0IixuX3JlYWwuY2VsbHMpCiAgICBjbGFzc2lmaWNhdGlvbnNbb3JkZXIocEFOTiRwQU5OWzE6bl9yZWFsLmNlbGxzXSwgZGVjcmVhc2luZz1UUlVFKVsxOm5FeHBdXSA8LSAiRG91YmxldCIKICAgIHNldUBtZXRhLmRhdGFbLCBwYXN0ZSgicEFOTiIscE4scEssbkV4cCxzZXA9Il8iKV0gPC0gcEFOTltyb3duYW1lcyhzZXVAbWV0YS5kYXRhKSwgMV0KICAgIHNldUBtZXRhLmRhdGFbLCBwYXN0ZSgiREYuY2xhc3NpZmljYXRpb25zIixwTixwSyxuRXhwLHNlcD0iXyIpXSA8LSBjbGFzc2lmaWNhdGlvbnMKICAgIHJldHVybihzZXUpCiAgfQp9CiMjIHVzYWdlOiBodHRwczovL3JkcnIuaW8vZ2l0aHViL2NocmlzLW1jZ2lubmlzLXVjc2YvRG91YmxldEZpbmRlci9tYW4vZG91YmxldEZpbmRlcl92My5odG1sCiMjIHNvdXJjZTogaHR0cHM6Ly9naXRodWIuY29tL2NocmlzLW1jZ2lubmlzLXVjc2YvRG91YmxldEZpbmRlci9ibG9iL21hc3Rlci9SL2RvdWJsZXRGaW5kZXJfdjMuUgpgYGAKClJ1biBEb3VibGV0RmluZGVyCmBgYHtyfQojIHRoZSB0dXRvcmlhbCByZWNvbW1lbmRzIHVzaW5nIHRoaXMgYXMgYW4gYXBwcm94aW1hdGlvbjoKI25FeHBfcG9pIDwtIHJvdW5kKDAuMTUqbnJvdyhwYl9zZXhAbWV0YS5kYXRhKSkKI2J1dCBhIG1vcmUgYXBwcm9wcmlhdGUgYXBwcm94aW1hdGlvbiBpcyB0aGF0IHRoZSBleHBlY3RlZCBudW1iZXIgb2YgZG91YmxldHMgaXMgfjElIHBlciAxMDAwIGNlbGxzIHNvOgpuRXhwX3BvaSA8LSByb3VuZCgoMC4wMSoobnJvdyhwYl9zZXhAbWV0YS5kYXRhKS8xMDAwKSkqbnJvdyhwYl9zZXhAbWV0YS5kYXRhKSkKI3J1biBkb3VibGV0IGZpbmRlcjoKcGJfc2V4IDwtIGRvdWJsZXRGaW5kZXJfdjMocGJfc2V4LCBQQ3MgPSAxOjIxLCBwTiA9IDAuMjUsIHBLID0gMC4wMSwgbkV4cCA9IG5FeHBfcG9pLCByZXVzZS5wQU5OID0gRkFMU0UsIHNjdCA9IEZBTFNFKQpgYGAKCnJlc3VsdHMgaW46CmBgYHtyfQp0YWJsZShwYl9zZXhAbWV0YS5kYXRhJERGLmNsYXNzaWZpY2F0aW9uc18wLjI1XzAuMDFfNDQwKQpgYGAKCiMjIyBWYWxpZGF0aW9uIGFuZCB2aXN1YWxpc2F0aW9uIG9mIGRvdWJsZXRzCgp2aXN1YWxpc2Ugd2hlcmUgZG91YmxldHMgYXJlOgpgYGB7cn0KZG91YmxldC5jZWxscyA8LSBjKHJvd25hbWVzKHBiX3NleEBtZXRhLmRhdGFbcGJfc2V4QG1ldGEuZGF0YSRERi5jbGFzc2lmaWNhdGlvbnNfMC4yNV8wLjAxXzQ0MCA9PSAiRG91YmxldCIsXSkpCmQxIDwtIERpbVBsb3QocGJfc2V4LCByZWR1Y3Rpb24gPSAidW1hcCIsIGNlbGxzLmhpZ2hsaWdodCA9IGRvdWJsZXQuY2VsbHMsIHNpemVzLmhpZ2hsaWdodCA9IDIpCiNUU05FUGxvdChvYmplY3QgPSBwYl9zZXgsIGNlbGxzLmhpZ2hsaWdodCA9IGRvdWJsZXQuY2VsbHMsIGRvLnJldHVybiA9IFRSVUUsICkKZG91YmxldDEgPC0gZDEgKyBjb29yZF9maXhlZCgpICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKQoKIyMgcGxvdCBjbHVzdGVycwpjbHVzdGVyX3Bsb3QgPC0gRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbCA9IFRSVUUpCgpkb3VibGV0MSArIGNsdXN0ZXJfcGxvdApgYGAKCmBgYHtyfQpWbG5QbG90KG9iamVjdCA9IHBiX3NleCwgZmVhdHVyZXMgPSAicEFOTl8wLjI1XzAuMDFfNDQwIiwgcHQuc2l6ZSA9IDAuMSkKYGBgCgpgYGB7cn0KIyMgZXh0cmFjdCBtZXRhIGRhdGEgY29scyBvZiBpbnRlcmVzdApkZiA8LSBwYl9zZXhAbWV0YS5kYXRhWyxjKCJSTkFfc25uX3Jlcy4xIiwiREYuY2xhc3NpZmljYXRpb25zXzAuMjVfMC4wMV80NDAiKV0KCiMjIG1ha2UgYSB0YWJsZSBmcm9tIGRvdWJsZXRzCmRmIDwtIGRhdGEuZnJhbWUocmJpbmQodGFibGUoZGYpKSkKCiMjIGFtbWVuZCByb3duYW1lcwpkZiRjbHVzdGVyIDwtIHJvd25hbWVzKGRmKQoKIyMgY2FsY3VsYXRlIHBlcmNlbnRhZ2VzIG9mIGNlbGxzIHRoYXQgYXJlIGRvdWJsZXRzCmRmJHBjX2RvdWJsZXQgPC0gKChkZlssMV0pLygoZGZbLDFdKSArIGRmWywyXSkpKjEwMAoKIyMgaW5zcGVjdAoja2FibGUoZGZbb3JkZXIoZGYkcGNfZG91YmxldCksXSkKCiMjIHBsb3QgCmdncGxvdChkYXRhPWRmLCBhZXMoeD1jbHVzdGVyLCB5PXBjX2RvdWJsZXQpKSArCiAgZ2VvbV9jb2woZmlsbD0ic3RlZWxibHVlIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBGaWx0ZXIgZG91YmxldHMKCkl0IGRlZmluaXRlbHkgc2VlbXMgbGlrZSB0aGVyZSBhcmUgc29tZSBiaWFzZXMgaW4gZG91YmxldCBkZXRlY3Rpb24uIEZld2VyIGRvdWJsZXRzIGluIHZlcnkgZWFybHkgcmluZ3MgYW5kIGluIG1hdHVyZSBzZXhlcyBtYXkgYmUgZHVlIHRvIGEgc21hbGxlciBudW1iZXIgb2YgdGhlIHBvcHVsYXRpb24gYmVpbmcgdGhlc2UgY2VsbHMuIAoKSXQgbWF5IGFsc28gYmUgYmlvbG9naWNhbCwgdGhhdCB0aGVzZSBjZWxscyBhcmUgbGVzcyBsaWtlbHkgdG8gYXNzb2NpYXRlIHRvIG9uZSBhbm90aGVyIChhbHRob3VnaCBsZXNzIGxpa2VseSwgYXMgZG91YmxldHMgYXJlIGEgcmVzdWx0IG9mIHRoZSBwcm9iYWJpbGl0eSBvZiB0d28gY2VsbHMgYmVpbmcgY2FwdHVyZWQgaW5zaWRlIHRoZSBzYW1lIGRyb3BsZXQgdG9nZXRoZXIgYXQgYSBjZXJ0YWluIGxvYWRpbmcgY29uY2VudHJhdGlvbiwgcmF0aGVyIHRoYW4gdHdvIGNlbGxzIGFscmVhZHkgYmVpbmcgdG9nZXRoZXIgdXBvbiBkcm9wbGV0IGNhcHR1cmUpLgoKcmVtb3ZlIGRvdWJsZXRzOgpgYGB7cn0KIyMgbWFrZSBhIGxpc3Qgb2Ygc2luZ2xldCBjZWxscwprZWVwX3NpbmdsZXRzIDwtIHJvd25hbWVzKHBiX3NleEBtZXRhLmRhdGFbcGJfc2V4QG1ldGEuZGF0YSRERi5jbGFzc2lmaWNhdGlvbnNfMC4yNV8wLjAxXzQ0MCA9PSAiU2luZ2xldCIsXSkKCiMjIHN1YnNldCBpbnRvIG5ldyBzZXVyYXQgb2JqZWN0CnBiX3NleF9maWx0ZXJlZCA8LSBzdWJzZXQocGJfc2V4LCBjZWxscyA9IGtlZXBfc2luZ2xldHMsIHN1YnNldC5yYXcgPSBUUlVFKQoKIyMgY29tcGFyZSBvbGQgYW5kIG5ldyBvYmplY3RzCnBiX3NleApwYl9zZXhfZmlsdGVyZWQKYGBgCgojIDcuIExpZmUgQ3ljbGUgU3RhZ2UgKFVzaW5nIEJ1bGsgUk5BLVNlcSBDb3JyZWxhdGlvbikgey50YWJzZXR9CgpBZGQgaW4gYnVsayBkYXRhIHByZWRpY3Rpb25zCgojIyMgaG9vIGV0IGFsLgpgYGB7cn0KI1BiIFByZWRpY3Rpb24gY29ycmVsYXRpb25zIHdpdGggYnVsayBkYXRhIChhc2V4dWFsIGhvbyk6IAoKI0xvYWQgaW4gcmVxdWlyZWQgcGFja2FnZToKbGlicmFyeShIbWlzYykKI0Nvb2VyY2UgZXhwcmVzc2lvbiBkYXRhIGludG8gYSBtYXRyaXggYW5kIGxvYWQgaW4gdGhlIHJlZmVyZW5jZSB0aW1lY291cnNlIGRhdGE6CngxMCA8LSBhcy5tYXRyaXgocGJfc2V4X2ZpbHRlcmVkQGFzc2F5cyRSTkFAZGF0YSkKcm93bmFtZXMoeDEwKSA8LSBnc3ViKCItIiwgIl8iLCByb3duYW1lcyh4MTApKQojcmVhZCBpbiBidWxrIGRhdGE6CmhvbzwtYXMubWF0cml4KHJlYWQudGFibGUoIi9Vc2Vycy9BbmR5L0dDU0tPL0dDU0tPX2FuYWx5c2lzX2dpdC9kYXRhL1JlZmVyZW5jZS9ob29fYmVyZzIudHh0IixoZWFkZXI9VCwgcm93Lm5hbWVzPTEpKQojTWFrZSBhIGJsYW5rIGRhdGFmcmFtZSBpbiB3aGljaCB0byBhZGQgcHJlZGljdGlvbjoKZGYgPC0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IDQsIG5yb3cgPSAwKSkKY29sbmFtZXMoZGYpIDwtIGMoIlByZWRpY3Rpb24oU3BlYXJtYW4pIiwicihTcGVhcm1hbikiLCJQcmVkaWN0aW9uKFBlYXJzb25zKSIsInIoUGVhcnNvbnMpIikKI0RvIGNvcnJlbGF0aW9ucyB3aXRoIGJ1bGsgZGF0YSB1c2luZyBib3RoIFNwZWFybWFuIGFuZCBQZWFyc29uIChhbmQgdGhlIHRvcCAxMDAwIGdlbmVzKToKZm9yIChpIGluIDE6bmNvbCh4MTApKQp7CiAgc2hhcmVkPC1pbnRlcnNlY3Qocm93Lm5hbWVzKGFzLm1hdHJpeChoZWFkKHNvcnQoeDEwWyxpXSwgZGVjcmVhc2luZz1UUlVFKSwxMDAwKSkpLHJvdy5uYW1lcyhob28pKQogIHN0ZXAwPC1yY29ycih4MTBbc2hhcmVkLGldLGhvb1tzaGFyZWQsMToxMl0sdHlwZSA9ICJzcGVhcm1hbiIpCiAgc3RlcDE8LWFzLm1hdHJpeCh0KHN0ZXAwJHJbMjoxMywxXSkpCiAgc3RlcDI8LXJjb3JyKHgxMFtzaGFyZWQsaV0saG9vW3NoYXJlZCwxOjEyXSx0eXBlID0gInBlYXJzb24iKQogIHN0ZXAzPC1hcy5tYXRyaXgodChzdGVwMiRyWzI6MTMsMV0pKQogIHN0ZXA0PC1jYmluZChjb2xuYW1lcyhzdGVwMSlbd2hpY2gubWF4KHN0ZXAxKV0sc3RlcDFbd2hpY2gubWF4KHN0ZXAxKV0sY29sbmFtZXMoc3RlcDMpW3doaWNoLm1heChzdGVwMyldLHN0ZXAzW3doaWNoLm1heChzdGVwMyldKQogIGNvbG5hbWVzKHN0ZXA0KSA8LSBjKCJQcmVkaWN0aW9uKFNwZWFybWFuKSIsInIoU3BlYXJtYW4pIiwiUHJlZGljdGlvbihQZWFyc29ucykiLCJyKFBlYXJzb25zKSIpCiAgcm93bmFtZXMoc3RlcDQpPC1jb2xuYW1lcyh4MTApW2ldCiAgZGY8LXJiaW5kKGRmLHN0ZXA0KQp9CiNXcml0ZSBvdXQgZGF0YSBpbnRvIGEgY3N2IGZpbGU6CiN3cml0ZS5jc3YoZGZyaW5ncixmaWxlPSIvVXNlcnMvYXIxOS9EZXNrdG9wL1BoRC9BUjA0X0dDU0tPX3Byb2plY3QvQWxsX211dGFudHNfRmViXzIwMTgvcHJlZGljdGlvbnBiY29tYmluZWQuY3N2IikKI0NoYW5nZSB0aGUgZm9ybWF0IG9mIHRoZSBvdXRwdXQgdG8gbWFrZSBpdCBtb3JlIHJlYWRhYmxlOgojZ3N1YigiUGJfIiwiIiwgZGZyaW5nclssMV0pIC0gTWFrZSBwcmVkaWN0aW9ucyBpbnRvIDE4aHIuZGF0IGZvcm1hdDoKCiNzcGVhcm1hbjoKZGZbLDFdIDwtIGdzdWIoIlBiXyIsIiIsIGRmWywxXSkKI1JlbW92ZSBoci5kYXQgZnJvbSBsaXN0OgpkZlssMV0gPC0gZ3N1YigiaHIuZGF0IiwiIiwgZGZbLDFdKQojQ2hlY2sgLSBkZnJpbmdyWywxXQojTWFrZSBpbnRvIGEgbnVtYmVyOgpkZlssMV0gPC0gYXMubnVtZXJpYyhkZlssMV0pCmRmWywyXSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZlssMl0pKQoKI3BlYXJzb246CmRmWywzXSA8LSBnc3ViKCJQYl8iLCIiLCBkZlssM10pCiNSZW1vdmUgaHIuZGF0IGZyb20gbGlzdDoKZGZbLDNdIDwtIGdzdWIoImhyLmRhdCIsIiIsIGRmWywzXSkKI0NoZWNrIC0gZGZyaW5nclssMV0KI01ha2UgaW50byBhIG51bWJlcjoKZGZbLDNdIDwtIGFzLm51bWVyaWMoZGZbLDNdKQpkZlssNF0gPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGZbLDRdKSkKI2FkZCB0byAxMFggb2JqZWN0OgpwYl9zZXhfZmlsdGVyZWQgPC0gQWRkTWV0YURhdGEocGJfc2V4X2ZpbHRlcmVkLCBtZXRhZGF0YSA9IGRmKQpgYGAKCiMjIyBLYXNpYSdzIGRhdGEKQ2FuIGFsc28gZG8gd2l0aCBLYXNpYSdzIHRpbWVjb3Vyc2UgZGF0YToKYGBge3J9CmthczwtYXMubWF0cml4KHJlYWQudGFibGUoIi9Vc2Vycy9BbmR5L0dDU0tPL0dDU0tPX2FuYWx5c2lzX2dpdC9kYXRhL1JlZmVyZW5jZS9BUDJPRVRDLnR4dCIsaGVhZGVyPVQsIHJvdy5uYW1lcz0xKSkKI01ha2UgYSBibGFuayBkYXRhZnJhbWUgaW4gd2hpY2ggdG8gYWRkIHByZWRpY3Rpb246CmRmcyA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gNCwgbnJvdyA9IDApKQpjb2xuYW1lcyhkZnMpIDwtIGMoIklEIiwiUHJlZGljdGlvbiIsInIgKFBlYXJzb24pIikKI0RvIGNvcnJlbGF0aW9ucyB3aXRoIGJ1bGsgZGF0YSB1c2luZyBib3RoIFNwZWFybWFuIGFuZCBQZWFyc29uIChhbmQgdGhlIHRvcCAxMDAwIGdlbmVzKToKZm9yIChpIGluIDE6bmNvbCh4MTApKQp7CiAgc2hhcmVkPC1pbnRlcnNlY3Qocm93Lm5hbWVzKGFzLm1hdHJpeChoZWFkKHNvcnQoeDEwWyxpXSwgZGVjcmVhc2luZz1UUlVFKSwxMDAwKSkpLHJvd25hbWVzKGthcykpCiAgc3RlcDA8LXJjb3JyKHgxMFtzaGFyZWQsaV0sa2FzW3NoYXJlZCwxOjEwXSx0eXBlID0gInNwZWFybWFuIikKICBzdGVwMTwtYXMubWF0cml4KHQoc3RlcDAkclsyOjExLDFdKSkKICBzdGVwMjwtcmNvcnIoeDEwW3NoYXJlZCxpXSxrYXNbc2hhcmVkLDE6MTBdLHR5cGUgPSAicGVhcnNvbiIpCiAgc3RlcDM8LWFzLm1hdHJpeCh0KHN0ZXAyJHJbMjoxMSwxXSkpCiAgc3RlcDQ8LWNiaW5kKGNvbG5hbWVzKHN0ZXAxKVt3aGljaC5tYXgoc3RlcDEpXSxzdGVwMVt3aGljaC5tYXgoc3RlcDEpXSxjb2xuYW1lcyhzdGVwMylbd2hpY2gubWF4KHN0ZXAzKV0sc3RlcDNbd2hpY2gubWF4KHN0ZXAzKV0pCiAgY29sbmFtZXMoc3RlcDQpIDwtIGMoIlByZWRpY3Rpb24oU3BlYXJtYW4pIiwicihTcGVhcm1hbikiLCJQcmVkaWN0aW9uKFBlYXJzb25zKSIsInIoUGVhcnNvbnMpIikKICByb3duYW1lcyhzdGVwNCk8LWNvbG5hbWVzKHgxMClbaV0KICBkZnM8LXJiaW5kKGRmcyxzdGVwNCkKfQojV3JpdGUgb3V0IGRhdGEgaW50byBhIGNzdiBmaWxlOgojd3JpdGUuY3N2KGRmLGZpbGU9Ii9Vc2Vycy9hcjE5L0Rlc2t0b3AvUGhEL0FSMDRfR0NTS09fcHJvamVjdC9BbGxfbXV0YW50c19GZWJfMjAxOC9wcmVkaWN0aW9ua2FzaWFjb21iaW5lZC5jc3YiKQoKI0NoYW5nZSB0aGUgZm9ybWF0IG9mIHRoZSBvdXRwdXQgdG8gbWFrZSBpdCBtb3JlIHJlYWRhYmxlOgojZ3N1YigiUGJfIiwiIiwgZGZzWywxXSkgLSBNYWtlIHByZWRpY3Rpb25zIGludG8gMThoci5kYXQgZm9ybWF0OgpkZnNbLDFdIDwtIGdzdWIoIlgiLCIiLCBkZnNbLDFdKQojTWFrZSBpbnRvIGEgbnVtYmVyOgpkZnNbLDFdIDwtIGFzLm51bWVyaWMoZGZzWywxXSkKI01ha2UgaW50byBhIG51bWJlcjoKZGZzWywyXSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZnNbLDJdKSkKCiNnc3ViKCJQYl8iLCIiLCBkZnNbLDFdKSAtIE1ha2UgcHJlZGljdGlvbnMgaW50byAxOGhyLmRhdCBmb3JtYXQ6CmRmc1ssM10gPC0gZ3N1YigiWCIsIiIsIGRmc1ssM10pCiNNYWtlIGludG8gYSBudW1iZXI6CmRmc1ssM10gPC0gYXMubnVtZXJpYyhkZnNbLDNdKQojZGZzWywxXQojTWFrZSBpbnRvIGEgbnVtYmVyOgpkZnNbLDRdIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmc1ssNF0pKQoKY29sbmFtZXMoZGZzKSA8LSBjKCdQcmVkaWN0aW9uKFNwZWFybWFuKV9LYXNpYScsICdyKFNwZWFybWFuKV9LYXNpYScsICdQcmVkaWN0aW9uKFBlYXJzb24pX0thc2lhJywgJ3IoUGVhcnNvbilfS2FzaWEnKQojYWRkIHRvIFNldXJhdDoKI2FkZCB0byAxMFggb2JqZWN0OgpwYl9zZXhfZmlsdGVyZWQgPC0gQWRkTWV0YURhdGEocGJfc2V4X2ZpbHRlcmVkLCBkZnMpCmBgYAoKIyMjIFZpc3VhbGlzZQoKQ29uZmlybSBsaWZlIGN5Y2xlIGRlc2lnbmF0aW9uczoKYGBge3J9CiMjIHBsb3QKRmVhdHVyZVBsb3QocGJfc2V4X2ZpbHRlcmVkLCBmZWF0dXJlcyA9IGMoIlByZWRpY3Rpb24uU3BlYXJtYW4uX0thc2lhIiwgIlByZWRpY3Rpb24uU3BlYXJtYW4uIikpCmBgYAoKb3B0aW1zZSBVTUFQCmBgYHtyfQojIyBQQ0EgY2FsYwpwYl9zZXhfZmlsdGVyZWQgPC0gUnVuUENBKHBiX3NleF9maWx0ZXJlZCwgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IHBiX3NleF9maWx0ZXJlZCkpCgojIyBlbGJvdyBwbG90CkVsYm93UGxvdChwYl9zZXhfZmlsdGVyZWQsIG5kaW1zID0gMzAsIHJlZHVjdGlvbiA9ICJwY2EiKQoKIyMgVU1BUCBjYWxjCnBiX3NleF9maWx0ZXJlZCA8LSBSdW5VTUFQKHBiX3NleF9maWx0ZXJlZCwgZGltcyA9IDE6OCwgc2VlZC51c2UgPSAzMDAsIG4ubmVpZ2hib3JzID0gNjAsIG1pbi5kaXN0ID0gMC41LCByZXB1bHNpb24uc3RyZW5ndGggPSAwLjA1LCBsb2NhbC5jb25uZWN0aXZpdHkgPSAyMCkKCiMjIFVNQVAgcGxvdApEaW1QbG90KHBiX3NleF9maWx0ZXJlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsID0gVFJVRSkKYGBgCgoKIyA4LiBTYXZlIGFuZCBFeHBvcnQgey50YWJzZXR9CgpTYXZlIGVudmlyb25tZW50CmBgYHtyfQojIyBUaGlzIHNhdmVzIGV2ZXJ5dGhpbmcgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudCBmb3IgZWFzeSByZWNhbGwgbGF0ZXIKI3NhdmUuaW1hZ2UoZmlsZSA9ICJHQ1NLT18xMFhfUUMuUkRhdGEiKQojbG9hZChmaWxlID0gIkdDU0tPXzEwWF9RQy5SRGF0YSIpCmBgYAoKU2F2ZSBvYmplY3QocykKYGBge3J9CiMjIHNhdmUgUmRhdGEKI3NhdmUocGJfMzBrX3NleF9maWx0ZXJlZCwgcGJfc2V4X2ZpbHRlcmVkLCBmaWxlID0gIlBhcnRfMl9pbnB1dC5SZGF0YSIpCiNsb2FkKGZpbGUgPSAiUGFydF8yX2lucHV0LlJkYXRhIikKCiMjIHNhdmUgUkRTCnNhdmVSRFMocGJfc2V4X2ZpbHRlcmVkLCBmaWxlID0gIi9Vc2Vycy9BbmR5L0dDU0tPL0dDU0tPX2FuYWx5c2lzX2dpdC9kYXRhX3RvX2V4cG9ydC9wYl9zZXhfZmlsdGVyZWQuUkRTIiwgY29tcHJlc3MgPSBGQUxTRSkgCiNwYl9zZXhfZmlsdGVyZWQgPC0gcmVhZFJEUygicGJfc2V4X2ZpbHRlcmVkLlJEUyIpCgojIyBTYXZlIFJvYmoKI3NhdmUocGJfc2V4X2ZpbHRlcmVkLGZpbGU9InBiX3NleF9maWx0ZXJlZC5Sb2JqIikKYGBgCgojIEFwcGVuZGl4IHsudGFic2V0fQoKIyMgU2Vzc2lvbiBJbmZvIApgYGB7ciwgZWNobyA9IEZBTFNFfQpzZXNzaW9uSW5mbygpCmBgYAo=